之前写过一篇简单的链表,现在更新些常用的手段。
- 快慢指针
(一)快指针走两步,慢指针走一步
1、找中点,那么slow是间点
struct ListNode*fast, *slow;
fast = slow = head;
while(fast != NULL && fast->next != NULL) {
slow= slow->next;
fast = fast->next->next;
}
2、如果fast能跟slow相遇,那么可以用来判断是循环指针。
快慢指针的相遇,假设快的指针走了2X步,那么慢指针走了X步;由于两个指针相遇了,说明有环,假设1个环有C步,那么2X-X=kC;
所以慢指针走的步数X是一个环的整数倍;既然慢指针走了环的整数倍,所以慢指针当前所在的位置就是在一个奇怪的位置(假设第一段长度如图是A,那么相遇点就是还差A那么多走到环口的位置)
3、此刻,让快指针从头开始走,跟慢指针都一步一步走,快指针重新走了Y步,慢指针以及走了X+Y步,那么必然相遇于环入口
struct ListNode*fast, *slow;
bool hasCycle(struct ListNode *head) {
fast = slow = head;
while(fast != NULL && fast->next != NULL) {
slow= slow->next;
fast = fast->next->next;
if(fast == slow) {
return true;
}
}
return false;
}
struct ListNode *detectCycle(struct ListNode *head) {
if(hasCycle(head) == false) {
return NULL;
}
fast = head;
while(fast!=slow) {
fast = fast->next;
slow = slow->next;
}
return fast;
}
(二)快指针先走Z步,用来查找倒数第Z个元素
1、举例寻找倒数K个数
struct ListNode* getKthFromEnd(struct ListNode* head, int k){
struct ListNode* fast, *slow;
fast = slow = head;
while(k) {
fast = fast->next;
k--;
}
while(fast!=NULL){
fast = fast->next;
slow = slow->next;
}
return slow;
}
2、申请节点指向head前,来处理head是单节点的问题
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *tmp = new ListNode(0, head);
ListNode *currNode = head;
ListNode *preNode = tmp;
for(int i = 0; i < n; i++) {
currNode = currNode->next;
}
while (currNode != NULL) {
currNode = currNode->next;
preNode = preNode->next;
}
preNode->next = preNode->next->next;
ListNode *ret = tmp->next;
delete tmp;
return ret;
}
};
- 链表翻转
需要一个指针NULL作为尾巴,每遍历一个节点,就把这个节点作为下一个的新尾巴;
struct ListNode *slownext = NULL;
struct ListNode *curr;
while(slow != NULL) {
curr = slow->next;
slow->next = slownext;
slownext = slow;
slow = curr;
}