赞
踩
判断一个链表是否带环
快慢指针法
创建一块一慢两个指针,去遍历链表,如果快指针走到了最后一个节点或走到了NULL,则此链表不带环。
如果带环,在进入环后,快慢指针会展开追逐,当快指针追到慢指针时,则此链表带环
这里我们让快指针一次走两步,慢指针一次走一步
- bool hasCycle(struct ListNode *head) {
- struct ListNode* fast = head,*slow = head;
-
-
- while(fast && fast->next)
- {
- slow = slow -> next;
- fast = fast->next->next;
- if(fast == slow)
- {
- return true;
- }
- }
-
- return false;
-
- }

想解释这个问题比较简单,我们假设有一带环链表List,和快指针 fast 慢指针 slow
此图为假设 slow 指针刚进入环时,与fast指针距离为N
因为fast指针的速度是slow指针速度的两倍,所以slow每走一步,fast都会追上slow一步
它们之间的距离也会不断的缩减:N N -1 N -1 .....3 2 1 0,当N为0时,fast就追上slow了,并不会出现错过的情况
这里我们只推导 slow一次走一步,fast 一次走3步的过程,其余推导过程类似
此图为假设 slow 指针刚进入环时,与fast指针距离为N
情况一:当N是偶数时 —— 直接追上
fast 追击 slow 情况:N N - 2 N - 3 ......4 2 0 刚好可以追上
情况二 :当 N 是奇数时 —— 展开新一轮的追击
结论:当 N 是奇数 且 C 为偶数时,永远也追不上
验证结论
经过推理我们得出:当 N 是奇数 且 C 为偶数时,fast 永远也追不上 slow,接下来我们尝试建立N与C的联系,来验证它是否正确
slow 刚进入圆环时,研究slow与fast走过的路程有:
slow 走的距离 :L
fast走的距离 :L + XC + C - N(X为圈数)
fast走的距离为 slow 的3倍 :3L = L + XC + C - N
化简该等式有:2L = (X + 1)C -N
将我们刚刚得出的结论(当 N 是奇数 且 C 为偶数时,永远也追不上)带入该等式,我们发现:
所以结论(当 N 是奇数 且 C 为偶数时,永远也追不上),不成立
当 slow 走一步 fast 走三步时 ,fast一定追的上 slow不存在永远追不上的情况!
找环的入口点
双指针法
先找到 fast指针 与 slow指针 相遇的节点meet ,如找不到返回NULL
找到meet节点后:让一个指针从头开始走,一个指针从meet开始走(两个指针走的速度一样),当两个指针相遇时,这个节点就是入口点
- struct ListNode * hasCycle(struct ListNode *head) {
- struct ListNode* fast = head,*slow = head;
-
- while(fast && fast->next)
- {
- slow = slow -> next;
- fast = fast->next->next;
- if(fast == slow)
- {
- return fast;
- }
- }
-
- return NULL;
-
- }
- struct ListNode *detectCycle(struct ListNode *head) {
- //找到meet节点
- struct ListNode* meet = hasCycle(head);
- if(meet == NULL)
- return NULL;
-
- //让慢指针重新开始走,快指针从meet位置开始走,当它们再次相遇时,就是环的入口点
- struct ListNode* cur_in = meet;//一个指针从meet开始走
- struct ListNode* cur_out = head;//一个指着从链表头部开始走
-
- while(true)
- {
- if(cur_in == cur_out)
- {
- return cur_in;
- }
- cur_in = cur_in->next;
- cur_out = cur_out->next;
- }
- }

研究快慢指针相遇走的路程时有:
slow走过的路程:L + N
fast走过的路程:L + XC + N (X大于等于1,且X为整数 )
fast走过的路程是slow的2倍:2(L + N) = L + XC + N
化简:2(L + N) = L + XC + N = L = XC - C = L = (X-1) + C - N
这个公式就是为什么两个相同速度的指针会在环的入口点相遇了:L = (X-1) + C - N
由于L = C - N所以它们才会在环的入口点相遇
在相遇点把环给解开,这样问题就变成了找两个链表相遇的问题
- /**
- * Definition for singly-linked list.
- * struct ListNode {
- * int val;
- * struct ListNode *next;
- * };
- */
- struct ListNode * hasCycle(struct ListNode *head) {
- struct ListNode* fast = head,*slow = head;
-
-
- while(fast && fast->next)
- {
- slow = slow -> next;
- fast = fast->next->next;
- if(fast == slow)
- {
- return fast;
- }
- }
- return NULL;
- }
- struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
-
- struct ListNode * curA;
- struct ListNode * curB;
- struct ListNode * cur;
-
- int countA = 0;
- int countB = 0;
-
- //统计A链表总节点
- cur = headA;
- while(cur)
- {
- cur = cur->next;
- countA++;
- }
-
- //统计B链表总结点
- cur = headB;
- while(cur)
- {
- cur = cur->next;
- countB++;
- }
-
- //让链表长的指针走到与链表短的指针同位置
- int gap = abs(countA - countB);
- struct ListNode * longList = headA,*shortList = headB;
- if(countB > countA)//修正长短链表
- {
- longList = headB;
- shortList = headA;
- }
-
- while(gap--)
- {
- longList = longList -> next;
- }
-
- //开始查找有没有相交的节点
- while(longList) //随意哪一个走到NULL就结束
- {
- if(longList == shortList) //节点地址相同说明这是相交的第一个节点
- {
- return longList;
- }
- longList = longList -> next;
- shortList = shortList -> next;
- }
-
- //一直没有相交的节点就返回NULL
- return NULL;
-
- }
-
- struct ListNode *detectCycle(struct ListNode *head) {
- //找到相遇点
- struct ListNode* meet = hasCycle(head);
- if(meet == NULL)
- {
- return NULL;
- }
-
- //解开环
- struct ListNode* newhead = meet->next;
- meet->next = NULL;
-
- //找两条链表相交点
- meet = getIntersectionNode(newhead,head);
- return meet;
-
- }

复制一个链表,难题在于链表中的random指针怎么去复制
由于random指针指向的是自身链表中的节点
你复制的链表节点的各自random指针也要和源链表一样,指向各自的节点
把复制链表依次插入到源链表各个节点中
这样,复制节点的random指针就相当于 == 被复制节点random指针指向的next节点
- struct Node* copyRandomList(struct Node* head) {
-
- if(head == NULL)
- {
- return NULL;
- }
-
- struct Node* cur = head;
-
- //将拷贝链表的节点插缝源链表中
- while(cur)
- {
- struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
- copy -> val = cur -> val;
-
- copy -> next = cur -> next;//将copy节点插入cur节点与cur下一个节点之间
- cur -> next = copy;
-
- cur = copy -> next;//cur走向源链表的下一个节点
- }
-
- //使用我们准备好的链表,它可以帮助我们建立random的联系
- cur = head;
- struct Node* copy;
- while(cur)//当走到NULL时停止,其实这个是拷贝链表最后一个节点的nxte指向NULL
- {
- copy = cur -> next;//创建copy指向每个源链表后的拷贝节点
- if(cur -> random == NULL)
- {
- copy -> random = NULL;
- }
- else
- {
- copy ->random = cur ->random ->next;//
- }
-
- cur = copy -> next;
- }
-
- //解开链表
- cur = head;//cur遍历源链表
- struct Node* source_next;//源链表的下一个节点
-
- struct Node* copy_sentry = (struct Node*)malloc(sizeof(struct Node));//哨兵节点
- struct Node* copy_tail;//拷贝链表尾节点
- copy_tail = copy_sentry;
-
-
-
- while(cur)
- {
- copy = cur -> next;
- source_next = copy->next;
-
- //拷贝链表的尾插
- copy_tail->next = copy;
- copy_tail = copy_tail -> next;
-
-
- //回复源链表
- cur->next = source_next;
-
- //cur和source_next在源链表上各走一步
- cur = source_next;
-
- }
-
- return copy_sentry->next;
-
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。