久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

C 鏈表

 飄渺o40uv5vs24 2023-02-13 發(fā)布于陜西

雖然C++中有l(wèi)ist容器,,但是在某些oj題中會出現(xiàn)有關(guān)鏈表的題,所以寫一篇C++鏈表,。

省去太過官方的定義,,只做最簡單易懂的介紹。

鏈表結(jié)構(gòu)

一個數(shù)據(jù)所在的內(nèi)存塊被分為兩個部分,,第一個部分放數(shù)據(jù),,而第二個部分則放下一個數(shù)據(jù)的地址,以此來連接各個數(shù)據(jù),,最后一個內(nèi)存塊放的地址為NULL,。這樣的一個內(nèi)存塊叫做節(jié)點(diǎn)。

在代碼中,,鏈表的一個節(jié)點(diǎn)是這樣的:

  1. struct ListNode
  2. {
  3. int data;
  4. ListNode* next;//結(jié)構(gòu)體指針
  5. };

鏈表有一定的缺陷:每存放一個數(shù)據(jù)都需要伴隨下一個數(shù)據(jù)的地址并且不支持隨機(jī)訪問,。

一,單鏈表

先來看最簡單的單鏈表,。

1.實(shí)現(xiàn)基本的增刪查改

  1. #include<algorithm>
  2. #include<iostream>
  3. #include<vector>
  4. using namespace std;
  5. struct ListNode
  6. {
  7. int data;
  8. ListNode* next;//結(jié)構(gòu)體指針
  9. };
  10. void Listprintf(ListNode* phead)
  11. {
  12. ListNode* cur=phead;
  13. while (cur != NULL)
  14. {
  15. cout << cur->data << '->';
  16. cur = cur->next;
  17. }
  18. }
  19. void Listpushback(ListNode** pphead, int x)
  20. {
  21. ListNode* newnode = new ListNode{ x,NULL };
  22. if (*pphead == NULL)
  23. {
  24. *pphead = newnode;
  25. }
  26. else
  27. {
  28. ListNode* tail= *pphead;
  29. while(tail->next != NULL)
  30. {
  31. tail = tail->next;
  32. }
  33. tail->next = newnode;
  34. }
  35. }
  36. void test_1()
  37. {
  38. ListNode* phead = NULL;
  39. Listpushback(&phead, 1);
  40. Listpushback(&phead, 2);
  41. Listpushback(&phead, 3);
  42. Listprintf(phead);
  43. }
  44. int main()
  45. {
  46. test_1();
  47. return 0;
  48. }

運(yùn)行結(jié)果:

在這段代碼中有一些需要注意的地方,,比如:

在Listpushback這個函數(shù)中,參數(shù)是二級指針,,如果寫成一級指針:

  1. void Listpushback(ListNode* pphead, int x)
  2. {
  3. ListNode* newnode = new ListNode{ x,NULL };
  4. if (pphead == NULL)
  5. {
  6. pphead = newnode;
  7. }
  8. else
  9. {
  10. ListNode* tail= pphead;
  11. while(tail->next != NULL)
  12. {
  13. tail = tail->next;
  14. }
  15. tail->next = newnode;
  16. }
  17. }

則結(jié)果會變成:

沒有任何輸出,。

原因是原本的指針phead是沒有任何指向的,,這個指針沒有指向某一個地址,而是一個空指針,,在函數(shù)傳參的時候,,如果參數(shù)pphead是一級指針,則pphead也是空指針,,改變pphead,,并不會影響到phead,所以最終一通操作下來,,phead還是空指針,,輸出結(jié)果也就是空的。

下面再增加一些功能:

  1. #include<algorithm>
  2. #include<iostream>
  3. #include<vector>
  4. using namespace std;
  5. struct ListNode
  6. {
  7. int data;
  8. ListNode* next;//結(jié)構(gòu)體指針
  9. };
  10. void Listprintf(ListNode* phead)
  11. {
  12. ListNode* cur=phead;
  13. while (cur != NULL)
  14. {
  15. cout << cur->data << '->';
  16. cur = cur->next;
  17. }
  18. cout << 'NULL' << endl;
  19. }
  20. //尾插
  21. void Listpushback(ListNode** pphead, int x)
  22. {
  23. ListNode* newnode = new ListNode{ x,NULL };
  24. if (*pphead == NULL)
  25. {
  26. *pphead = newnode;
  27. }
  28. else
  29. {
  30. ListNode* tail= *pphead;
  31. while(tail->next != NULL)
  32. {
  33. tail = tail->next;
  34. }
  35. tail->next = newnode;
  36. }
  37. }
  38. //頭插
  39. void Listpushfront(ListNode** pphead, int x)
  40. {
  41. ListNode* newnode = new ListNode{ x,NULL };
  42. newnode->next = *pphead;
  43. *pphead = newnode;
  44. }
  45. //尾刪
  46. void Listpopback(ListNode** pphead)
  47. {
  48. if (*pphead == NULL)
  49. {
  50. return;
  51. }
  52. if ((*pphead)->next == NULL)
  53. {
  54. delete(*pphead);
  55. *pphead = NULL;
  56. }
  57. else
  58. {
  59. ListNode* tail = *pphead;
  60. ListNode* prev = NULL;
  61. while (tail->next)
  62. {
  63. prev = tail;
  64. tail = tail->next;
  65. }
  66. delete(tail);
  67. tail = NULL;
  68. prev->next = NULL;
  69. }
  70. }
  71. //頭刪
  72. void Listpopfront(ListNode** pphead)
  73. {
  74. if (*pphead == NULL)
  75. {
  76. return;
  77. }
  78. else
  79. {
  80. ListNode* newnode = (*pphead)->next;
  81. delete(*pphead);
  82. *pphead = newnode;
  83. }
  84. }
  85. //查找元素,,返回值是地址
  86. ListNode* Listfind(ListNode* phead, int x)
  87. {
  88. ListNode* cur = phead;
  89. while (cur)
  90. {
  91. if (cur->data == x)
  92. {
  93. return cur;
  94. }
  95. else
  96. {
  97. cur = cur->next;
  98. }
  99. }
  100. return NULL;
  101. }
  102. //插入元素,,在pos的前一個位置插入
  103. //配合Listfind使用,具體使用見test_insert函數(shù)
  104. void Listinsert(ListNode** phead, ListNode* pos, int x)
  105. {
  106. ListNode* newnode = new ListNode{ x,NULL };
  107. if (*phead == pos)
  108. {
  109. newnode->next = (*phead);
  110. *phead = newnode;
  111. }
  112. else
  113. {
  114. ListNode* posprev = *phead;
  115. while (posprev->next != pos)
  116. {
  117. posprev = posprev->next;
  118. }
  119. posprev->next = newnode;
  120. newnode->next = pos;
  121. }
  122. }
  123. //單鏈表并不適合在前一個位置插入,,因?yàn)檫\(yùn)算較麻煩,,會損失效率
  124. //包括c++中為單鏈表提供的庫函數(shù)也只有一個insert_after而沒有前一個位置插入
  125. //在后一個位置插入相對簡單
  126. void Listinsert_after(ListNode** phead, ListNode* pos, int x)
  127. {
  128. ListNode* newnode = new ListNode{ x,NULL };
  129. newnode->next = pos->next;
  130. pos->next = newnode;
  131. }
  132. //刪除指定位置的節(jié)點(diǎn)
  133. void Listerase(ListNode** pphead, ListNode* pos)
  134. {
  135. if (*pphead == pos)
  136. {
  137. *pphead = pos->next;
  138. delete(pos);
  139. }
  140. else
  141. {
  142. ListNode* prev = *pphead;
  143. while (prev->next!=pos)
  144. {
  145. prev = prev->next;
  146. }
  147. prev->next = pos->next;
  148. delete(pos);
  149. }
  150. }
  151. //釋放鏈表
  152. void Listdestory(ListNode** pphead)
  153. {
  154. ListNode* cur = *pphead;
  155. while(cur)
  156. {
  157. ListNode* next = cur->next;
  158. delete(cur);
  159. cur = next;
  160. }
  161. *pphead = NULL;
  162. }
  163. void test_insert()
  164. {
  165. ListNode* phead = NULL;
  166. Listpushback(&phead, 1);
  167. Listpushback(&phead, 2);
  168. Listpushback(&phead, 3);
  169. Listprintf(phead);
  170. ListNode* pos = Listfind(phead, 2);
  171. if (pos != NULL)
  172. {
  173. Listinsert(&phead, pos, 20);
  174. }
  175. Listprintf(phead);
  176. pos = Listfind(phead, 2);
  177. if (pos != NULL)
  178. {
  179. Listinsert_after(&phead, pos, 20);
  180. }
  181. Listprintf(phead);
  182. Listdestory(&phead);
  183. }
  184. void test_find()
  185. {
  186. ListNode* phead = NULL;
  187. Listpushback(&phead, 1);
  188. Listpushback(&phead, 2);
  189. Listpushback(&phead, 3);
  190. Listprintf(phead);
  191. ListNode* pos = Listfind(phead, 2);
  192. if (pos != NULL)
  193. {
  194. pos->data = 20;//Listfind不僅能查找,也能借此修改,,這也是函數(shù)返回地址的原因
  195. }
  196. Listprintf(phead);
  197. Listdestory(&phead);
  198. }
  199. void test_erase()
  200. {
  201. ListNode* phead = NULL;
  202. Listpushback(&phead, 1);
  203. Listpushback(&phead, 2);
  204. Listpushback(&phead, 3);
  205. Listprintf(phead);
  206. ListNode* pos = Listfind(phead, 2);
  207. if (pos != NULL)
  208. {
  209. Listerase(&phead, pos);
  210. }
  211. Listprintf(phead);
  212. Listdestory(&phead);
  213. }
  214. void test_pop_and_push()
  215. {
  216. ListNode* phead = NULL;
  217. Listpushback(&phead, 1);
  218. Listpushback(&phead, 2);
  219. Listpushback(&phead, 3);
  220. Listprintf(phead);
  221. Listpushfront(&phead, 1);
  222. Listpushfront(&phead, 2);
  223. Listpushfront(&phead, 3);
  224. Listprintf(phead);
  225. Listpopback(&phead);
  226. Listpopfront(&phead);
  227. Listprintf(phead);
  228. Listdestory(&phead);
  229. }
  230. int main()
  231. {
  232. //test_pop_and_push();
  233. test_find();
  234. //test_insert();
  235. //test_erase();
  236. return 0;
  237. }

test_pop_and_push()測試結(jié)果:

 test_find()測試結(jié)果:

 test_insert()測試結(jié)果:

 test_erase()測試結(jié)果:

 2.對鏈表進(jìn)行一些操作

(1)刪除等于給定值的所有節(jié)點(diǎn),。

例如:在一條鏈表中刪除值為6的節(jié)點(diǎn)。

  1. #include<algorithm>
  2. #include<iostream>
  3. #include<vector>
  4. using namespace std;
  5. struct ListNode
  6. {
  7. int data;
  8. ListNode* next;//結(jié)構(gòu)體指針
  9. };
  10. void Listprintf(ListNode* phead)
  11. {
  12. ListNode* cur=phead;
  13. while (cur != NULL)
  14. {
  15. cout << cur->data << '->';
  16. cur = cur->next;
  17. }
  18. cout << 'NULL' << endl;
  19. }
  20. void Listpushback(ListNode** pphead, int x)
  21. {
  22. ListNode* newnode = new ListNode{ x,NULL };
  23. if (*pphead == NULL)
  24. {
  25. *pphead = newnode;
  26. }
  27. else
  28. {
  29. ListNode* tail= *pphead;
  30. while(tail->next != NULL)
  31. {
  32. tail = tail->next;
  33. }
  34. tail->next = newnode;
  35. }
  36. }
  37. ListNode* creatlist()
  38. {
  39. ListNode* phead = NULL;
  40. Listpushback(&phead, 1);
  41. Listpushback(&phead, 9);
  42. Listpushback(&phead, 6);
  43. Listpushback(&phead, 8);
  44. Listpushback(&phead, 6);
  45. Listpushback(&phead, 2);
  46. Listpushback(&phead, 3);
  47. return phead;
  48. }
  49. ListNode* removeElements(ListNode* head, int x)
  50. {
  51. ListNode* prev = NULL;
  52. ListNode* cur = head;
  53. while (cur)
  54. {
  55. if (cur->data == x)
  56. {
  57. if (cur == head)//如果第一個元素就是要刪除的,,進(jìn)行頭刪
  58. {
  59. head = cur->next;
  60. delete(cur);
  61. cur = head;
  62. }
  63. else
  64. {
  65. prev->next = cur->next;
  66. delete(cur);
  67. cur = prev->next;
  68. }
  69. }
  70. else
  71. {
  72. prev = cur;
  73. cur = cur->next;
  74. }
  75. }
  76. return head;
  77. }
  78. int main()
  79. {
  80. ListNode*phead = creatlist();//先創(chuàng)建一條鏈表
  81. Listprintf(phead);
  82. phead = removeElements(phead, 6);//刪除值為6的節(jié)點(diǎn)
  83. Listprintf(phead);
  84. return 0;
  85. }

 自測結(jié)果:

 當(dāng)然如果是一條全為6的鏈表也是可以完成刪除的:

這里再說一下為什么刪除元素和創(chuàng)建鏈表這兩個函數(shù)的參數(shù)不是二級指針,,因?yàn)檫@兩個函數(shù)是要返回一個指針的。 

(2)翻轉(zhuǎn)鏈表

比如:將1->2->3->4->5翻轉(zhuǎn)為5->4->3->2->1

翻轉(zhuǎn)鏈表的方式很多,,這里只寫兩種作為參考:

第一種,,翻轉(zhuǎn)指針。

這種方法的邏輯是定義三個指針,,一個用來紀(jì)錄當(dāng)前位置的前一個位置,,一個用來紀(jì)錄當(dāng)前位置,一個用來紀(jì)錄當(dāng)前位置的后一個位置,,再通過將當(dāng)前位置指向下一個位置的指針修改為指向上一個位置,,再往后迭代,以達(dá)到翻轉(zhuǎn)鏈表的目的,。

為什么要三個指針呢,,因?yàn)閷?dāng)前位置指向前一個位置之后,就找不到當(dāng)前位置的下一個位置了,,所以需要第三個指針來指向下一個位置,。

代碼部分由于只有測試函數(shù)不一樣,其余代碼差異不大,,所以僅貼出測試函數(shù)部分:

  1. ListNode* reverseList(ListNode* head)
  2. {
  3. if (head == NULL)
  4. {
  5. return NULL;
  6. }
  7. ListNode* prev, * cur, * next;
  8. prev = NULL;
  9. cur = head;
  10. next = cur->next;
  11. while (cur)
  12. {
  13. cur->next = prev;//翻轉(zhuǎn)指針
  14. //往后迭代
  15. prev = cur;
  16. cur = next;
  17. if (next)//這里是因?yàn)楫?dāng)cur指向最后一個節(jié)點(diǎn)的時候,,next就已經(jīng)是NULL了,這個時候如果再執(zhí)行next=next->next則會出現(xiàn)錯誤
  18. {
  19. next = next->next;
  20. }
  21. }
  22. return prev;
  23. }

自測結(jié)果:

測試結(jié)果(測試網(wǎng)站:??途W(wǎng)):

 第二種方式,,創(chuàng)建新鏈表進(jìn)行頭插

這種方法的邏輯是將原鏈表中的節(jié)點(diǎn)取下來頭插到新鏈表newlist中,同樣的,,需要三個指針,,一個為NULL,即為新鏈表newlist,,一個紀(jì)錄原鏈表從頭開始的地址,,一個紀(jì)錄下一個位置。將原鏈表第一個節(jié)點(diǎn)取下來對newlist進(jìn)行頭插,,再取第二個第三個,,往后迭代。

  1. ListNode* reverseList(ListNode* head)
  2. {
  3. ListNode* cur = head;
  4. ListNode* newlist = NULL;
  5. ListNode* next = NULL;
  6. while (cur)
  7. {
  8. next = cur->next;
  9. //頭插
  10. cur->next = newlist;
  11. newlist = cur;
  12. //往后迭代
  13. cur = next;
  14. }
  15. return newlist;
  16. }

自測結(jié)果:

 測試結(jié)果(測試網(wǎng)站:??途W(wǎng)):

(3) 返回中間節(jié)點(diǎn)的地址

如果有兩個中間節(jié)點(diǎn),,返回第二個中間節(jié)點(diǎn)。

這種操作比較簡單,,最容易想到的方法就是先遍歷一次整個鏈表找出有幾個節(jié)點(diǎn),,再遍歷一次找出中間節(jié)點(diǎn)

但是如果要求只能遍歷一次呢,所以這里不采用遍歷兩次的方法,,而采用快慢指針的方式只遍歷一次,。

邏輯就是定義兩個指針,一個走的更慢,,一次走一步,,另一個走的更快,一次走兩步,。

  1. ListNode* middleNode(ListNode* head)
  2. {
  3. ListNode* slow, * fast;
  4. slow = fast = head;
  5. while (fast && fast->next)
  6. {
  7. slow = slow->next;
  8. fast = fast->next->next;
  9. }
  10. return slow;
  11. }

自測結(jié)果:

(4)倒數(shù)第k個節(jié)點(diǎn) 

輸入一個鏈表和k,,輸出從倒數(shù)第k個節(jié)點(diǎn)到最后一個。

思路和快慢指針相似,,定義兩個指針,,一個指針先走k步。

  1. ListNode* findK(ListNode* head, int k)
  2. {
  3. ListNode* fast, * slow;
  4. fast = slow = head;
  5. while(k--)
  6. {
  7. if (fast == NULL)//如果當(dāng)fast等于NULL時k仍不為0,,則k大于鏈表長度
  8. {
  9. return NULL;
  10. }
  11. fast = fast->next;
  12. }
  13. while (fast)
  14. {
  15. fast = fast->next;
  16. slow = slow->next;
  17. }
  18. return slow;
  19. }

自測結(jié)果:

測試結(jié)果(測試網(wǎng)站:??途W(wǎng)):

 (5)合并有序鏈表

 思路比較簡單,依次比較鏈表中的節(jié)點(diǎn),,將較小的節(jié)點(diǎn)尾插到新鏈表,。

當(dāng)然還有一種做法是將一個鏈表直接插入到另一個鏈表中,這種方式畫圖畫起來倒是簡單,,但是實(shí)際寫起來很麻煩,,這里不推薦這種寫法,,也不會寫這種。

  1. ListNode* mergeTwoList(ListNode* l1, ListNode* l2)
  2. {
  3. if (l1 == NULL)//如果一個鏈表為空,,則返回另一個
  4. {
  5. return l2;
  6. }
  7. if (l2 == NULL)
  8. {
  9. return l1;
  10. }
  11. ListNode* head = NULL;
  12. ListNode* tail = NULL;
  13. while (l1 && l2)
  14. {
  15. if (l1->data < l2->data)
  16. {
  17. if (head == NULL)
  18. {
  19. head = tail =l1;
  20. }
  21. else
  22. {
  23. tail->next = l1;
  24. tail = l1;
  25. }
  26. l1 = l1->next;
  27. }
  28. else
  29. {
  30. if (head == NULL)
  31. {
  32. head = tail = l2;
  33. }
  34. else
  35. {
  36. tail->next = l2;
  37. tail = l2;
  38. }
  39. l2 = l2->next;
  40. }
  41. }
  42. if (l1)
  43. {
  44. tail->next = l1;
  45. }
  46. if(l2)
  47. {
  48. tail->next = l2;
  49. }
  50. return head;
  51. }

自測結(jié)果:

我這里是將兩條1->2->3->4->5->6的鏈表合并,。

測試結(jié)果(測試網(wǎng)站:牛客網(wǎng)):

 可以看到這種不帶哨兵位的寫法也還是比較麻煩,,要判斷頭為不為空,,所以下面再寫一種帶哨兵位的寫法。

帶哨兵位(也叫帶頭)就是我們?nèi)ソo鏈表多定義一個頭結(jié)點(diǎn),,這個節(jié)點(diǎn)不存儲有效數(shù)據(jù),。

  1. ListNode* mergeTwoList(ListNode* l1, ListNode* l2)
  2. {
  3. if (l1 == NULL)//如果一個鏈表為空,則返回另一個
  4. {
  5. return l2;
  6. }
  7. if (l2 == NULL)
  8. {
  9. return l1;
  10. }
  11. ListNode* head = NULL, * tail = NULL;
  12. head = tail = new ListNode;//哨兵位的頭結(jié)點(diǎn)
  13. while (l1 && l2)
  14. {
  15. if (l1->data < l2->data)
  16. {
  17. tail->next = l1;
  18. tail = l1;
  19. l1 = l1->next;
  20. }
  21. else
  22. {
  23. tail->next = l2;
  24. tail = l2;
  25. l2 = l2->next;
  26. }
  27. }
  28. if (l1)
  29. {
  30. tail->next = l1;
  31. }
  32. if (l2)
  33. {
  34. tail->next = l2;
  35. }
  36. ListNode* list = head->next;
  37. delete(head);
  38. return list;
  39. }

自測結(jié)果:

測試結(jié)果(測試網(wǎng)站:??途W(wǎng)):

 (6)分割鏈表

 給定一個值x,,將所有小于x的節(jié)點(diǎn)排在其余節(jié)點(diǎn)之前,且不能改變原來的數(shù)據(jù)順序,,返回重新排列后的鏈表頭指針,。

思路:定義兩條鏈表,一條將小于x的所有節(jié)點(diǎn)按照原順序連接成鏈表,,另一條將大于x的所有節(jié)點(diǎn)按照原順序連接成鏈表,,最后再合起來。

  1. ListNode* partition(ListNode* phead, int x)
  2. {
  3. ListNode* lesshead, * lesstail, * greaterhead, * greatertail;
  4. lesshead = lesstail = new ListNode;//定義一個哨兵位頭結(jié)點(diǎn),,方便尾插
  5. lesstail->next = NULL;
  6. greaterhead = greatertail = new ListNode;
  7. greatertail->next = NULL;
  8. ListNode* cur = phead;
  9. while (cur)
  10. {
  11. if (cur->data < x)
  12. {
  13. lesstail->next = cur;
  14. lesstail = cur;
  15. }
  16. else
  17. {
  18. greatertail->next = cur;
  19. greatertail = cur;
  20. }
  21. cur = cur->next;
  22. }
  23. lesstail->next = greaterhead->next;
  24. greatertail->next = NULL;//舉個例子,,這樣一條鏈表:1->4->15->5,現(xiàn)在給的x是6,那么排序后15應(yīng)該在最后,,正因如此,,重新排序后15的next是沒變的,仍然指向5,,不手動將next改為NULL,,就會成環(huán),無限排下去,。
  25. ListNode* newhead = lesshead->next;
  26. delete(lesshead);
  27. delete(greaterhead);
  28. return newhead;
  29. }

自測結(jié)果:

測試結(jié)果(測試網(wǎng)站:力扣):

(7)鏈表回文

判斷鏈表是否回文,。

思路,先找到一條鏈表的中點(diǎn),,將后半段逆置,,設(shè)置兩個指針,一個從頭開始,,一個從逆置后的頭開始,,逐個判斷直到最后。

圖:

節(jié)點(diǎn)奇數(shù)個:

節(jié)點(diǎn)偶數(shù)個:

代碼:

  1. ListNode* middleNode(ListNode* head)
  2. {
  3. ListNode* slow, * fast;
  4. slow = fast = head;
  5. while (fast && fast->next)
  6. {
  7. slow = slow->next;
  8. fast = fast->next->next;
  9. }
  10. return slow;
  11. }
  12. ListNode* reverseList(ListNode* head)
  13. {
  14. ListNode* cur = head;
  15. ListNode* newlist = NULL;
  16. ListNode* next = NULL;
  17. while (cur)
  18. {
  19. next = cur->next;
  20. //頭插
  21. cur->next = newlist;
  22. newlist = cur;
  23. //往后迭代
  24. cur = next;
  25. }
  26. return newlist;
  27. }
  28. bool check(ListNode* head)
  29. {
  30. ListNode* mid = middleNode(head);
  31. ListNode* rhead = reverseList(mid);
  32. ListNode* curHead = head;
  33. ListNode* curRhead = rhead;
  34. while(curHead&&curRhead)
  35. {
  36. if (curHead->data != curRhead->data)
  37. return false;
  38. else
  39. {
  40. curHead = curHead->next;
  41. curRhead = curRhead->next;
  42. }
  43. }
  44. return true;
  45. }

自測結(jié)果:

測試結(jié)果(測試網(wǎng)站:??途W(wǎng)): 

 

(8)鏈表相交 

 給兩個單鏈表的頭結(jié)點(diǎn),,找出并返回兩個單鏈表相交的起始節(jié)點(diǎn),,沒有交點(diǎn)則返回null。

最簡單粗暴的思路就是將A鏈表的每個節(jié)點(diǎn)和B鏈表的每個節(jié)點(diǎn)挨著挨著比較,,這里不使用這一種,。 

還有一種思路就是,找到A和B的尾結(jié)點(diǎn)對比,,如果一樣則有交點(diǎn),如果不一樣則沒有交點(diǎn),,有交點(diǎn)的情況下又該怎樣找到相交的起始節(jié)點(diǎn),?

如果兩條鏈表在相交之前的節(jié)點(diǎn)個數(shù)一樣的話,那么找到相交的起始節(jié)點(diǎn)就很簡單,,定義兩個指針同時向前走直到相等即可,,所以我們在找鏈表A和B的尾結(jié)點(diǎn)的時候,同時紀(jì)錄下A和B的長度,,用長的減去短的得到一個數(shù)x,,再分別為兩條鏈表定義頭指針,讓長的那條鏈表的頭指針先走x步,,那么就可以當(dāng)做是在相交之前節(jié)點(diǎn)數(shù)一樣了,。

  1. ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2)
  2. {
  3. if(pHead1==NULL)
  4. {
  5. return NULL;
  6. }
  7. if(pHead2==NULL)
  8. {
  9. return NULL;
  10. }
  11. ListNode* tail1=pHead1;
  12. ListNode* tail2=pHead2;
  13. int len1=1,len2=1;
  14. while(tail1->next)
  15. {
  16. len1++;
  17. tail1=tail1->next;
  18. }
  19. while(tail2->next)
  20. {
  21. len2++;
  22. tail2=tail2->next;
  23. }
  24. if(tail1!=tail2)//不相交
  25. {
  26. return NULL;
  27. }
  28. int gap=abs(len1-len2);
  29. ListNode* longlist=pHead1;
  30. ListNode* shortlist=pHead2;
  31. if(len1<len2)
  32. {
  33. longlist=pHead2;
  34. shortlist=pHead1;
  35. }
  36. while(gap--)//長的先走差距步,再同時走找交點(diǎn)
  37. {
  38. longlist=longlist->next;
  39. }
  40. while(longlist!=shortlist)
  41. {
  42. longlist=longlist->next;
  43. shortlist=shortlist->next;
  44. }
  45. return longlist;
  46. }

測試結(jié)果(測試網(wǎng)站:??途W(wǎng)):

 (9)環(huán)形鏈表

判斷鏈表是否帶環(huán),,并且返回環(huán)的入口節(jié)點(diǎn)。

判斷是否帶環(huán)的思路比較簡單,,定義兩個指針,,一個走得快,一次走兩步,,一個走得慢,,一次走一步,如果不帶環(huán),,那么走得快的最終會走到NULL,,如果帶環(huán),那么走得快的會和走得慢的相遇,。

要找環(huán)的入口節(jié)點(diǎn),,也需要定義兩個指針,一個從鏈表頭開始,,一個從快慢指針相遇的位置開始,,他們同時出發(fā),當(dāng)這兩個指針相遇時,,所在的位置就是入口節(jié)點(diǎn),。

這里做簡單的證明,,假設(shè)鏈表頭到入口節(jié)點(diǎn)距離是L,快慢指針相遇的位置meetnode距離入口節(jié)點(diǎn)為x

 假設(shè)C是環(huán)的長度,,當(dāng)快慢指針相遇時,,快指針走的距離是L+N*C+x,N是快指針走的圈數(shù),,慢指針走的距離是L+x,,因?yàn)榭熘羔樢淮巫邇刹剑羔樢淮巫咭徊?,所以快指針走的距離是慢指針的兩倍,,所以有:

L+N*C+x=2(L+x)

得到:

L+x=N*C

L=(N-1)*C+C-x

其中C-x即為meetnode到入口節(jié)點(diǎn)的距離,所以當(dāng)頭指針和從meetnode出發(fā)的指針相遇時,,頭指針走了L,,從meetnode出發(fā)的指針走了C-x,相遇點(diǎn)即為入口節(jié)點(diǎn),。

代碼:

  1. ListNode* EntryNodeOfLoop(ListNode* pHead) {
  2. ListNode*fast=pHead;
  3. ListNode*slow=pHead;
  4. while(fast&&fast->next)
  5. {
  6. slow=slow->next;
  7. fast=fast->next->next;
  8. if(fast==slow)
  9. {
  10. ListNode*meet=fast;
  11. while(meet!=pHead)
  12. {
  13. meet=meet->next;
  14. pHead=pHead->next;
  15. }
  16. return pHead;
  17. }
  18. }
  19. return NULL;
  20. }

測試結(jié)果(測試網(wǎng)站:??途W(wǎng)):

這里再說下還有另一種處理方式,就是將meetnode作為鏈表的尾,,meetnode的next作為鏈表的頭,,就轉(zhuǎn)化成了兩條相交鏈表找第一個相交節(jié)點(diǎn)的問題,這種方式寫起來會麻煩一些,,這里也不做代碼實(shí)現(xiàn),。

二,雙向鏈表

 雙向鏈表的一個數(shù)據(jù)所在的內(nèi)存塊是被分成了三部分,,除了像單鏈表那樣一部分存儲數(shù)據(jù),,一部分存儲下一個數(shù)據(jù)的地址之外,還要有一部分來存儲上一個數(shù)據(jù)的地址,。

在代碼中如下:

  1. struct ListNode
  2. {
  3. int data;
  4. ListNode* next;//結(jié)構(gòu)體指針
  5. ListNode* prev;
  6. };

雙向鏈表的結(jié)構(gòu):

其中最常用的是雙向帶頭(哨兵位)循環(huán)鏈表

 

后面的代碼中雙向鏈表也都是這種,。

1.增刪查改

  這里多說一句,很多人都會下意識的認(rèn)為在帶哨兵位的鏈表中,,哨兵位是一條鏈表開始的位置,,但是實(shí)際上哨兵位的下一位才是。

  1. #include<algorithm>
  2. #include<iostream>
  3. #include<vector>
  4. using namespace std;
  5. struct ListNode
  6. {
  7. int data;
  8. ListNode* next;//結(jié)構(gòu)體指針
  9. ListNode* prev;
  10. };
  11. ListNode* Initlist()//初始化雙向帶頭(哨兵)循環(huán)鏈表
  12. {
  13. ListNode* phead=new ListNode;
  14. phead->next = phead;
  15. phead->prev = phead;//定義一個哨兵位,,自己的next和prev指向自己
  16. return phead;
  17. }
  18. void pushback(ListNode* phead,int x)//尾插
  19. {
  20. ListNode* tail = phead->prev;//循環(huán)鏈表,,哨兵位的前一個就是尾
  21. ListNode* newhead = new ListNode;
  22. newhead->data = x;
  23. newhead->next = phead;
  24. newhead->prev = tail;
  25. tail->next = newhead;
  26. phead->prev = newhead;
  27. }
  28. void popback(ListNode* phead)//尾刪
  29. {
  30. if (phead->next == phead)//鏈表為空,不能再刪了
  31. return;
  32. ListNode* tail = phead->prev;
  33. ListNode* tailprev = tail->prev;
  34. tailprev->next = phead;
  35. phead->prev = tailprev;
  36. delete(tail);
  37. }
  38. void pushfront(ListNode* phead,int x)//頭插
  39. {
  40. ListNode* next = phead->next;
  41. ListNode* newnode = new ListNode;
  42. newnode->data = x;
  43. phead->next = newnode;
  44. newnode->prev = phead;
  45. newnode->next = next;
  46. next->prev = newnode;
  47. }
  48. void popfront(ListNode* phead)//頭刪
  49. {
  50. if (phead->next == phead)//鏈表為空,,不能再刪了
  51. return;
  52. ListNode* next = phead->next;
  53. ListNode* dnext = next->next;
  54. phead->next = dnext;
  55. dnext->prev = phead;
  56. delete(next);
  57. }
  58. ListNode* listFind(ListNode* phead, int x)//查找
  59. {
  60. ListNode* cur = phead->next;
  61. while (cur != phead)
  62. {
  63. if (cur->data == x)
  64. return cur;
  65. cur = cur->next;
  66. }
  67. return NULL;
  68. }
  69. void insertList(ListNode* pos, int x)//指定位置插入
  70. {
  71. ListNode* prev = pos->prev;
  72. ListNode* newnode = new ListNode;
  73. newnode->data = x;
  74. prev->next = newnode;
  75. newnode->prev = prev;
  76. newnode->next = pos;
  77. pos->prev = newnode;
  78. }
  79. void eraseList(ListNode* pos)//指定位置刪除
  80. {
  81. ListNode* posNext = pos->next;
  82. ListNode* posPrev = pos->prev;
  83. posNext->prev = posPrev;
  84. posPrev->next = posNext;
  85. delete(pos);
  86. pos = NULL;
  87. }
  88. void printlist(ListNode* phead)
  89. {
  90. ListNode* cur = phead->next;
  91. while (cur != phead)
  92. {
  93. cout << cur->data << ' ';
  94. cur = cur->next;
  95. }
  96. cout << endl;
  97. }
  98. void test_pop_push()
  99. {
  100. ListNode* phead = Initlist();
  101. pushback(phead, 1);
  102. pushback(phead, 2);
  103. pushback(phead, 3);
  104. printlist(phead);
  105. popback(phead);
  106. popback(phead);
  107. printlist(phead);
  108. pushfront(phead, 1);
  109. pushfront(phead, 2);
  110. pushfront(phead, 3);
  111. printlist(phead);
  112. popfront(phead);
  113. popfront(phead);
  114. printlist(phead);
  115. }
  116. void test_find_insert_erase()
  117. {
  118. ListNode* phead = Initlist();
  119. pushback(phead, 1);
  120. pushback(phead, 2);
  121. pushback(phead, 3);
  122. printlist(phead);
  123. ListNode* p = listFind(phead, 2);
  124. insertList(p, 20);
  125. printlist(phead);
  126. p = listFind(phead, 20);
  127. eraseList(p);
  128. printlist(phead);
  129. }
  130. int main()
  131. {
  132. //test_pop_push();
  133. test_find_insert_erase();
  134. return 0;
  135. }

test_pop_push()測試結(jié)果:

test_find_insert_erase()測試結(jié)果:

其中最重要的是insert()和erase()函數(shù),,在雙向帶頭循環(huán)鏈表中,這兩個函數(shù)是可以分別和頭尾插,頭尾刪函數(shù)復(fù)用的,,即可以修改成:

  1. void pushback(ListNode* phead,int x)//尾插
  2. {
  3. insertList(phead,x);
  4. }
  5. void popback(ListNode* phead)//尾刪
  6. {
  7. if (phead->next == phead)//鏈表為空,,不能再刪了
  8. return;
  9. eraseList(phead->prev);
  10. }
  11. void pushfront(ListNode* phead,int x)//頭插
  12. {
  13. insertList(phead->next, x);
  14. }
  15. void popfront(ListNode* phead)//頭刪
  16. {
  17. if (phead->next == phead)//鏈表為空,不能再刪了
  18. return;
  19. eraseList(phead->next);
  20. }

最后再說一下為什么雙向鏈表中的函數(shù)的參數(shù)是一級指針而不是像之前單鏈表那樣是二級指針的問題 ,,這是因?yàn)樵谖覍懙碾p向鏈表中是帶了頭的,,也就是帶了哨兵位,所以不需要考慮傳進(jìn)來的是空指針,,改變形參不會影響變量的問題,,而在我寫單鏈表的時候是沒有帶頭的。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多