001_链表

  1. // 001_链表.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
  2. //
  3. #include "pch.h"
  4. #include <iostream>
  5. //
  6. // 链表的数据结构
  7. // 1. 链表是由每一个节点连接起来的.
  8. struct Node {
  9. int nData; // 数据域
  10. Node* pNext; // 指针域,用于保存下一个节点的首地址
  11. };
  12. class List {
  13. // 记录当前链表的节点个数
  14. int m_nCount=0;
  15. // 用于保存链表的头节点的地址
  16. Node* m_pHead = nullptr;
  17. public:
  18. /*!
  19. * 函数名 : insert
  20. * 功 能 : 将元素nData插入到链表的第nIndex位置
  21. * 参 数 : int nIndex 插入到的下标
  22. * 参 数 : int nData 要插入的数据
  23. * 返回值 : bool
  24. */
  25. bool insert(int nIndex, int nData)
  26. {
  27. // 1. 检查插入位置是否错误.
  28. if (nIndex <0 || nIndex>m_nCount) {
  29. return false;
  30. }
  31. // 2. 在堆空间中申请内存来保存新的节点
  32. Node* pNew = new Node;
  33. // 2.1 将要插入到链表中的数据保存在新节点.
  34. pNew->nData = nData;
  35. // 3. 在链表中找到插入位置的前一个节点.
  36. // 3.1 如果插入到的是第0个位置.那么需要将
  37. // 新节点的首地址记录在m_pHead指针中.
  38. if (nIndex == 0) {
  39. pNew->pNext = m_pHead;
  40. m_pHead = pNew;
  41. }
  42. else {
  43. // 循环遍历到插入位置的前一个节点.
  44. Node* pPre = m_pHead;
  45. for (int i = 1; i < nIndex; ++i) {
  46. pPre = pPre->pNext;
  47. }
  48. // 4. 将前一个节点的pNext字段保存到新节点的pNext字段
  49. // 这样就可以让新节点记录下一个节点
  50. pNew->pNext = pPre->pNext;
  51. // 5. 将新节点的首地址保存到前一个节点的pNext字段
  52. // 这样通过前一个节点就能找到新的节点.
  53. pPre->pNext = pNew;
  54. }
  55. // 6. 增加节点个数.
  56. ++m_nCount;
  57. }
  58. /*!
  59. * 函数名 : remove
  60. * 功 能 : 从链表中删除下标为nIndex的元素
  61. * 参 数 : int nIndex 要删除的元素所在的下标
  62. * 返回值 : void
  63. */
  64. void remove(int nIndex)
  65. {
  66. // 1. 判断下标是否正确
  67. if (nIndex < 0 || nIndex >= m_nCount) {
  68. return;
  69. }
  70. // 2. 找到要删除的元素的前一个节点
  71. // 2.1 如果删除的是第0个节点. 这个节点的首地址
  72. // 是保存在m_pHead头节点指针中.
  73. // 因此, 直接操作头节点指针.
  74. if (nIndex == 0) {
  75. // m_pHead->pNext记录的是被删除节点的下一个
  76. // 节点的首地址
  77. // 现在将下一个节点作为新的头节点:将节点的地址
  78. // 记录在头节点指针中.
  79. Node* temp = m_pHead;
  80. m_pHead = m_pHead->pNext;
  81. delete temp;
  82. }
  83. else {
  84. Node* pPreNode = m_pHead;
  85. for (int i = 1; i < nIndex; ++i) {
  86. pPreNode = pPreNode->pNext;
  87. }
  88. // 3. 通过前一个节点的pNext字段找到被删除节点
  89. // 的首地址
  90. Node* pRemoveNode = pPreNode->pNext;
  91. // 4. 再通过被删除节点的pNext字段,得到被删除
  92. // 节点的下一个节点的首地址
  93. Node* pNextNode = pRemoveNode->pNext;
  94. // 5. 将被删除节点的下一个节点的首地址保存到
  95. // 被删除节点的前一个节点的pNext字段.
  96. pPreNode->pNext = pNextNode;
  97. // 6. 释放掉被删除节点的内存空间
  98. delete pRemoveNode;
  99. }
  100. // 7. 递减当前节点个数
  101. --m_nCount;
  102. }
  103. /*!
  104. * 函数名 : find
  105. * 功 能 : 在链表中查找元素
  106. * 参 数 : int nData 被查找的元素
  107. * 返回值 : int 元素在链表中的下标,找不到了就返回-1
  108. */
  109. int find(int nData) {
  110. Node* pNode = m_pHead;
  111. int nIndex = 0;
  112. while (pNode != nullptr) {
  113. // 判断当前遍历到的节点保存的数据
  114. // 是否是要查找的数据
  115. if (pNode->nData == nData) {
  116. // 如果是,就返回下标
  117. return nIndex;
  118. }
  119. // 递增下标,用于记录,当前遍历到了第几个元素
  120. ++nIndex;
  121. // 找到下一个节点
  122. pNode = pNode->pNext;
  123. }
  124. return -1;
  125. }
  126. /*!
  127. * 函数名 : at
  128. * 功 能 : 获取指定下标的元素的数据域的引用.
  129. * 参 数 : int nIndex 元素所在的下标
  130. * 返回值 : int& 返回数据的引用.这样就可以在函数外部修改节点的数据了
  131. */
  132. int& at(int nIndex) {
  133. if (nIndex < 0 || nIndex >= m_nCount) {
  134. throw std::exception("下标越界");
  135. }
  136. // 遍历到指定下标处的节点
  137. Node* pNode = m_pHead;
  138. for (int i = 0; i < nIndex; ++i) {
  139. pNode = pNode->pNext;
  140. }
  141. // 返回节点的引用(返回值是int&引用类型)
  142. return pNode->nData;
  143. }
  144. /*!
  145. * 函数名 : push_back
  146. * 功 能 : 将数据插入到链表最后一个位置
  147. * 参 数 : int nData
  148. * 返回值 : void
  149. */
  150. void push_back(int nData) {
  151. insert(m_nCount, nData);
  152. }
  153. /*!
  154. * 函数名 : pop_back
  155. * 功 能 : 将最后一个元素的值输出到*pData,然后删除
  156. * 参 数 : int * pData
  157. * 返回值 : void
  158. */
  159. void pop_back(int* pData) {
  160. if (m_nCount) {
  161. // 先获取最后一个元素的值,并保存到
  162. // 指针指向的内存中
  163. *pData = at(m_nCount - 1);
  164. // 删除该元素
  165. remove(m_nCount - 1);
  166. }
  167. }
  168. /*!
  169. * 函数名 : push_front
  170. * 功 能 : 将数据插入到链表的最前面
  171. * 参 数 : int nData
  172. * 返回值 : void
  173. */
  174. void push_front(int nData) {
  175. insert(0, nData);
  176. }
  177. /*!
  178. * 函数名 : pop_front
  179. * 功 能 : 删除链表最前面的数据, 删除前,将值输出到*pData
  180. * 参 数 : int * pData
  181. * 返回值 : void
  182. */
  183. void pop_front(int * pData) {
  184. if (m_nCount) {
  185. // 先获取最后一个元素的值,并保存到
  186. // 指针指向的内存中
  187. *pData = at( 0 );
  188. // 删除该元素
  189. remove( 0 );
  190. }
  191. }
  192. /*!
  193. * 函数名 : print
  194. * 功 能 : 打印链表中的所有元素
  195. * 返回值 : void
  196. */
  197. void print() {
  198. Node* pNode = m_pHead;
  199. printf("[");
  200. while (pNode != nullptr) {
  201. // 打印节点的数据域
  202. printf("%d,", pNode->nData);
  203. // 找到下一个节点
  204. pNode = pNode->pNext;
  205. }
  206. printf("\b]\n");
  207. }
  208. };
  209. int main(){
  210. List l;
  211. printf("测试插入功能:\n");
  212. l.insert(0, 10);
  213. l.insert(0, 9);
  214. l.insert(2, 12);
  215. l.insert(1, 11);
  216. l.print();
  217. // 修改链表的下标为2的项
  218. printf("\n测试修改功能:\n");
  219. int& rEle = l.at(2);
  220. printf("下标为2项的值:%d\n", rEle);
  221. rEle = 100;
  222. printf("下标为2项的值:%d\n", rEle);
  223. printf("\n测试查找功能:\n");
  224. int nIndex = l.find(12);
  225. printf("元素12在链表中的第%d项\n", nIndex);
  226. printf("\n测试删除功能:\n");
  227. // 删除元素9
  228. // 1. 先通过find查找到元素9的下标
  229. // 2. 在通过删除函数删除指定下标的元素
  230. l.remove(l.find(9));
  231. l.remove(3);// 9- 11 - 10 -12
  232. l.print();
  233. l.remove(1);
  234. l.print();
  235. l.remove(0);
  236. l.print();
  237. // 模拟栈的操作:
  238. printf("\n模拟栈操作:\n");
  239. int nData = 0;
  240. l.push_back(10);
  241. l.push_back(9);
  242. l.push_back(8);
  243. l.push_back(7);
  244. l.print();
  245. l.pop_back(&nData);
  246. l.print();
  247. l.pop_back(&nData);
  248. l.print();
  249. l.pop_back(&nData);
  250. l.print();
  251. l.pop_back(&nData);
  252. l.print();
  253. // 模拟队列的操作
  254. printf("\n模拟队列操作:\n");
  255. l.push_back(100);
  256. l.push_back(101);
  257. l.push_back(102);
  258. l.push_back(103);
  259. l.pop_front(&nData);
  260. l.print();
  261. l.pop_front(&nData);
  262. l.print();
  263. l.pop_front(&nData);
  264. l.print();
  265. l.pop_front(&nData);
  266. l.print();
  267. }

二分法

  1. // 二分法.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
  2. //
  3. #include "pch.h"
  4. #include <iostream>
  5. int bin_seach(int* pArr, int nCount, int nData)
  6. {
  7. //1. 先确定开始下标和结束的下标
  8. int nBeg = 0;
  9. int nEnd = nCount - 1;
  10. int nMid = (nEnd - nBeg) / 2;
  11. int i = 1;
  12. while (1)
  13. {
  14. // 判断中间位置保存的数据是否就是
  15. // 要查找的元素
  16. if (pArr[nMid] == nData) {
  17. return nMid;
  18. }
  19. if (nBeg == nEnd) {
  20. return -1;
  21. }
  22. // 开始确定下一次的查找范围
  23. if (nData < pArr[nMid]) {
  24. // 1. 保存开始下标不变
  25. // 2. 将当前中间下标设置为新的结束下标
  26. nEnd = nMid;
  27. }
  28. else {
  29. // 1. 保存结束的下标不变
  30. // 2. 将当前中间下标设置为新的开始下标
  31. nBeg = nMid;
  32. }
  33. // 重新计算新的中间下标
  34. nMid = ((nEnd - nBeg) / 2) + nBeg;
  35. printf("循环了%d次\n", i++);
  36. }
  37. }
  38. void sort(int *pArr, int nCount) {
  39. for (int i = 0; i < nCount - 1; ++i) {
  40. for (int j = 0; j < nCount - i - 1; ++j) {
  41. if (pArr[j] > pArr[j + 1]) {
  42. int temp = pArr[j];
  43. pArr[j] = pArr[j + 1];
  44. pArr[j + 1] = temp;
  45. }
  46. }
  47. }
  48. }
  49. void printArr(int *pArr, int nCount) {
  50. for (int i = 0; i < nCount; ++i) {
  51. printf("%d,", pArr[i]);
  52. }
  53. printf("\b\n");
  54. }
  55. int main()
  56. {
  57. int nArr[] = { 10,5,6,8,9,45,65,32,45 };
  58. printArr(nArr, _countof(nArr));
  59. sort(nArr, _countof(nArr));
  60. printArr(nArr, _countof(nArr));
  61. int nIndex = bin_seach(nArr, _countof(nArr), 45);
  62. printf("45在%d\n", nIndex);
  63. }