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; } };