1. 程式人生 > >LeetCode連結串列專題

LeetCode連結串列專題

# 連結串列 ## 套路總結 1.多個指標 移動 2.虛假連結串列頭:凡是有可能刪除頭節點的都建立一個虛擬頭節點,程式碼可以少一些判斷(需要用到首部前一個元素的時候就加虛擬頭指標) ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181326283-1873869331.png) 3.快慢指標 如leetcode160 快慢指標找連結串列環的起點 ## [19. 刪除連結串列的倒數第N個節點](https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/) 題目要求:只掃描一遍 刪除連結串列,肯定要找到被刪節點的前一個節點 ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181338072-270523563.png) 1.找到倒數第n個節點的前一個節點(倒數第n+1) 2.雙指標 first指標指向第k個,second頭指標指向虛假頭節點,兩個指標一起移動,當first指標指向最後一個節點的時候(first下一個節點為NULL),就說明second到達了倒數第k個節點 3.刪除即可 second ->next = second->next->next ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181358371-2135603489.png) ``` /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { auto dummy = new ListNode(-1); dummy->next = head; auto first = dummy; auto second = dummy; while(n--) first = first->next; while(first->next != NULL){ second = second->next; first = first->next; } second->next = second->next->next; return dummy->next; } }; ``` ## [237. 刪除連結串列中的節點](https://leetcode-cn.com/problems/delete-node-in-a-linked-list/) 例如,給定node指向5這個點,刪除5這個點 ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181417499-271679002.png) 真正意義刪除要知道被刪除節點的上一個點 假裝刪除,把這個點的值偽裝成下一個點的值,把下一個點刪掉即可 ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181426245-2008673654.png) ``` /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: void deleteNode(ListNode* node) { if(node->next){ node->val = node->next->val; node->next = node->next->next; } return; } }; ``` C++語法把node兩個屬性的值都一起替換為下一個節點的屬性 ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181506998-2000772734.png) ``` *(node) = *(node->next); ``` ## [83. 刪除排序連結串列中的重複元素](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/) ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181528775-1088849965.png) ``` /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* deleteDuplicates(ListNode* head) { auto *first = head; while(first && first->next){ if(first->val == first->next->val){ first->next = first->next->next; }else{ first = first->next; //這裡first可能移動到了空 所以要判斷first是否空 } } return head; } }; ``` ## [82. 刪除排序連結串列中的重複元素 II](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii/) ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181540656-2031492072.png) ``` /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* deleteDuplicates(ListNode* head) { auto dummy = new ListNode(-1); dummy->next = head; auto pre = dummy,cur = pre->next; int cnt = 0; while(pre && cur){ cnt = 0; auto nxt = cur->next; while(nxt && nxt->val == cur->val) { cnt++; nxt = nxt->next; } if(cnt >= 1){ pre->next = nxt; cur = pre->next; }else{ pre = pre->next; cur = pre->next; } } return dummy->next; } }; ``` ## [61. 旋轉連結串列](https://leetcode-cn.com/problems/rotate-list/) 兩個指標,距離為k (不需要用到虛擬頭節點,頭節點會改變時用到) ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181555604-1612082789.png) ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181610671-906616598.png) 之後讓first->next指向開頭head,再讓head指向現在的頭(second->next)! 再讓second->next指向空 ``` /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* rotateRight(ListNode* head, int k) { if(!head) return NULL; int n = 0; for(auto p = head;p;p=p->next) n++; k %= n; auto first = head,second = head; while(k--) first = first->next; while(first->next){ first=first->next; second=second->next; } first->next = head; head = second->next; second->next = NULL; return head; } }; ``` ## [24. 兩兩交換連結串列中的節點](https://leetcode-cn.com/problems/swap-nodes-in-pairs/) 1.建立虛擬頭節點,因為頭節點可能會改變 2.三個指標 ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181729498-931225769.png) ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181736726-2031490892.png) ``` /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* swapPairs(ListNode* head) { auto dummy = new ListNode(-1); dummy->next = head; for(auto p = dummy;p->next && p->next->next;){ auto a = p->next,b = a->next; p->next = b; a->next = b->next; b->next = a; p = a; //指向下一個新的兩對前的最後一個點 } return dummy->next; } }; ``` ## [206. 反轉連結串列](https://leetcode-cn.com/problems/reverse-linked-list/) 兩個翻轉指標a,b;一個保留指標c保留b後面的鏈防止被刪除,不需要虛擬頭節點因為不需要用到首部前一個 分三步 ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181747874-27439047.png) ``` /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* reverseList(ListNode* head) { if(!head) return NULL; auto a = head,b = head->next; while(b){ auto c = b->next; b->next = a; a = b; b = c; } head->next = NULL;//原來頭是原來的第一節點 現在的最後一個節點所以指向空 head = a; return head; } }; ``` ## [92. 反轉連結串列 II](https://leetcode-cn.com/problems/reverse-linked-list-ii/) 1.因為頭節點會發生變化,設定虛擬頭節點 2.a指標移動到翻轉前一個點,b指標移動第一個翻轉的點,d指標移動到最後一個翻轉的點。c指標指向最後一個翻轉的點的下一個點。然後翻轉b~d之間的點和206題一樣 3.連線a->d,b->c ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181804457-930602045.png) ``` /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* reverseBetween(ListNode* head, int m, int n) { if(m == n) return head; auto dummy = new ListNode(-1); //虛擬頭節點 dummy->next = head; //找到a和d auto a = dummy,d = dummy; for(int i=0;inext;//不設定虛擬頭節點的話,如果n=1就找不到了a } for(int i=0;inext; //找到b和c auto b = a->next, c = d->next; //翻轉b和d之間的數字 for(auto first = b->next,second = b; first != c;){ auto third = first->next; first->next = second; second = first,first = third; } //連線 b->next = c; a->next = d; return dummy->next; } }; ``` ## [160. 相交連結串列](https://leetcode-cn.com/problems/intersection-of-two-linked-lists/) 相遇:當指標p和指標q走的路程相等時相遇 考慮都走a+b+c的倍數,肯定會相遇 ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181818443-1807127934.png) ``` /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { auto tempHeadA = headA; auto tempHeadB = headB; while(tempHeadA != tempHeadB){ if(tempHeadA) tempHeadA = tempHeadA->next; else tempHeadA = headB; if(tempHeadB) tempHeadB = tempHeadB->next; else tempHeadB = headA; } return tempHeadB; } }; ``` ## [142. 環形連結串列 II](https://leetcode-cn.com/problems/linked-list-cycle-ii/) 快慢指標 1.快指標慢指標從head頭部出發,fast快指標每次走兩步,slow慢指標每次走一步直到相遇。 2.把其中一個指標移動到head頭部,快慢指標再每次走一步直到相遇,相遇帶你即為答案; ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181830327-522966649.png) 證明:利用快指標走動過的是慢指標的二倍,假設環起點座標為x,第一次相遇點距離換起點距離為y。 可列公式2×(x+n1×c+y)=x+y+n2×c ,化簡得x+y=(n2-n1)×c。 大白話說就是:非環部分的長度+環起點到相遇點之間的長度就是環的整數倍。 即x+y為環的整數倍 那麼第一次相遇時我們現在距離環起點為y,所以只要再走x就到環起點了 再走x的話就讓一個指標從head走,另一個從第一次相遇點走,每次都走1步 ``` /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *detectCycle(ListNode *head) { auto fast = head,slow = head; while(fast && fast->next){ fast = fast->next; fast = fast->next; //快指標移動兩次 slow = slow->next; //慢指標移動1次 if(fast == slow){ //當快慢指標相遇時退出 break; } } if(fast==NULL || fast->next == NULL) return NULL; else{ slow = head; //讓其中一個指標移動到頭部 while(fast != slow){ //再走到相遇點即可 fast = fast->next; slow = slow->next; } return slow; } } }; ``` ## [148. 排序連結串列](https://leetcode-cn.com/problems/sort-list/) 要求空間常數,時間O(nlogn) 因為快排用到遞迴(棧),空間為logn;遞迴版歸併空間消耗大;所以用迭代版歸併 自底向上程式碼寫法:先列舉長度為2,分成一半,左右歸併;再列舉長度為4... ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181844750-803851198.png) ![](https://img2020.cnblogs.com/blog/1454456/202005/1454456-20200516181851197-233748152.png) ``` /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* sortList(ListNode* head) { int n = 0; for(auto p = head; p ; p = p -> next) n++; auto dummy = new ListNode(-1); dummy->next = head; for(int i=1; inext; //左半段邊界指標 auto right = cur->next; //右半段邊界指標 for(int k=0;knext; int l = 0,r = 0; while(l < i && r < i && right){ //歸併比較左右哪個大 if(left->
val <= right-> val){ cur->next = left; cur = left; left = left->next; l++; }else{ cur->next = right; cur = right; right = right->next; r++; } } //一個先到了末尾 所以要拼接另一端的剩餘部分 while(l < i){ cur->next = left; cur = left; left = left->next; l++; } while(r < i && right){ cur->next = right; cur = right; right = right->next; r++; } cur->next = right; //拼接下一段 這裡的right最終指向了下一段的left } } return dummy->next; } }; ``` ## [21. 合併兩個有序連結串列](https://leetcode-cn.com/problems/merge-two-sorted-lists/) (線性合併) O(n)O(n) 1.新建頭部的保護結點 dummy,設定 cur 指標指向 dummy。 2.如果p的值比q小,就將cur->next = p,否則讓cur -> next = q (選小的先連線) 迴圈以上步驟直到 l1l1 或 l2l2 為空。 3.將剩餘的 p或 q連 接到 cur 指標後邊。 ``` /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { auto dummmy = new ListNode(-1); auto cur = dummmy; auto p = l1,q = l2; //選小的優先 while(p && q){ if(p->val <= q->val){ cur->next = p; cur = p; p = p->next; }else{ cur->next = q; cur = q; q = q->next; } } //加入剩餘 while(p){ cur->next = p; p = p->next; } while(q){ cur->next = q; q = q->next; } // cur->next = (p != NULL ? p : q); return dummmy->next; }