1.多個指針 移動node
2.虛假鏈表頭:凡有可能刪除頭節點的都建立一個虛擬頭節點,代碼能夠少一些判斷(須要用到首部前一個元素的時候就加虛擬頭指針)指針
3.快慢指針code
如leetcode160 快慢指針找鏈表環的起點blog
題目要求:只掃描一遍排序
刪除鏈表,確定要找到被刪節點的前一個節點遞歸
1.找到倒數第n個節點的前一個節點(倒數第n+1)leetcode
2.雙指針rem
first指針指向第k個,second頭指針指向虛假頭節點,兩個指針一塊兒移動,當first指針指向最後一個節點的時候(first下一個節點爲NULL),就說明second到達了倒數第k個節點get
3.刪除便可 second ->next = second->next->nextit
/** * 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; } };
例如,給定node指向5這個點,刪除5這個點
真正意義刪除要知道被刪除節點的上一個點
僞裝刪除,把這個點的值假裝成下一個點的值,把下一個點刪掉便可
/** * 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兩個屬性的值都一塊兒替換爲下一個節點的屬性
*(node) = *(node->next);
/** * 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; } };
/** * 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; } };
兩個指針,距離爲k
(不須要用到虛擬頭節點,頭節點會改變時用到)
以後讓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; } };
1.創建虛擬頭節點,由於頭節點可能會改變
2.三個指針
/** * 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; } };
兩個翻轉指針a,b;一個保留指針c保留b後面的鏈防止被刪除,不須要虛擬頭節點由於不須要用到首部前一個
分三步
/** * 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; } };
1.由於頭節點會發生變化,設置虛擬頭節點
2.a指針移動到翻轉前一個點,b指針移動第一個翻轉的點,d指針移動到最後一個翻轉的點。c指針指向最後一個翻轉的點的下一個點。而後翻轉b~d之間的點和206題同樣
3.鏈接a->d,b->c
/** * 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;i<m-1;i++) { a = a->next;//不設置虛擬頭節點的話,若是n=1就找不到了a } for(int i=0;i<n;i++) d = d->next; //找到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; } };
相遇:當指針p和指針q走的路程相等時相遇
考慮都走a+b+c的倍數,確定會相遇
/** * 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; } };
快慢指針
1.快指針慢指針從head頭部出發,fast快指針每次走兩步,slow慢指針每次走一步直到相遇。
2.把其中一個指針移動到head頭部,快慢指針再每次走一步直到相遇,相遇帶你即爲答案;
證實:利用快指針走動過的是慢指針的二倍,假設環起點座標爲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; } } };
要求空間常數,時間O(nlogn)
由於快排用到遞歸(棧),空間爲logn;遞歸版歸併空間消耗大;因此用迭代版歸併
自底向上代碼寫法:先枚舉長度爲2,分紅一半,左右歸併;再枚舉長度爲4...
/** * 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; i<n ; i*=2){ //枚舉每一段的一半長 auto cur = dummy; for(int j=0; j+i<n ; j+=i*2){ auto left = cur->next; //左半段邊界指針 auto right = cur->next; //右半段邊界指針 for(int k=0;k<i;k++) right = right->next; 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; } };
(線性合併) 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; } };