目录
2.基于上题,判断是否成环,若成环则返回第一个成环的节点,否则返回NULL
方法二:转化为链表相交(但本题要求不改变链表结构,所以代码仅供大家参考)
一、环形链表
1.判断链表是否成环
题目描述:给一个链表头指针,设计一个算法,如何判断其是否成环,成环返回true,否则返回false
思路分析:采用快慢指针方式,慢指针一次一步,快指针一次走两步,如果成环,由于快指针走得快,最终它们必然在环中相遇;如果不成环,快指针最终为空。分别判断返回即可
bool hasCycle(struct ListNode* head) {
struct ListNode *fast = head, *slow = head;
if (head == NULL) {
return false;
}
while (fast && fast->next) // 循环条件要使链表不成环时能够跳出,必须两个条件同时满足,因为fast一次走两步,不成环有两种情况,fast为最后一个节点,fast为最后一个节点的指针域(即NULL)
{
fast = fast->next->next;
slow = slow->next;
if (slow == fast) {
return true;
}
}
return false;
}
-->fast能否一次走三步,此时能否相遇,此情况下存在永不相遇的情况吗?
当慢指针进入环时,不妨假设fast与slow相距N,进行如下分析:
显然如果N为偶数,则快慢指针最终能相遇,而如果N为奇数,则slow进入时fast的第一轮追击将错过,但我们不能立刻断定此情况下不会相遇,因为还存在第二轮,甚至第三轮追击。
接下来对第二轮追击进行分析:(注意之所以有第二轮是因为N为奇数)
不妨假设环的长度为C,则第二轮追击开始时,fast与slow相距C-1,根据前面分析知道,如果C-1为偶数,则能相遇,如果C-1为奇数,则又将错过而陷入死循环中,因此问题在于C-1为偶数还是奇数。
由于N为奇数,则C不可能为偶数,否则等式不可能成立,即C必然为奇数,所以C-1必然为偶数,故第二轮必然能相遇。
CL:fast能够一次走三步,无论N为奇数还是偶数,最终都能相遇。区别在于N为偶数,追一轮相遇,N为奇数,追两轮相遇。
-->fast能否一次走更多步呢?方法类似,不过更为复杂,大家可以自行分析
2.基于上题,判断是否成环,若成环则返回第一个成环的节点,否则返回NULL
方法一:数理证明
同样方法判断是否成环,成环则快慢指针相遇,此时给出一指向头节点的指针cur,让cur和slow同时移动,一次一步,它们必然相遇,且相遇时节点为第一个成环节点,返回即可。
接下来证明一下cur和slow相遇节点为第一个成环节点
代码如下:
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode*fast=head,*slow=head;
struct ListNode*cur=head;
while(fast && fast->next)
{
fast=fast->next->next;//快指针一次走两步
slow=slow->next;//慢指针一次走一步
if(fast==slow)//如果成环必然在环中相遇
{
while(cur!=slow)//相遇跳出
{
cur=cur->next;
slow=slow->next;
}
return cur;//返回第一个成环节点
}
}
return NULL;
}
方法二:转化为链表相交(但本题要求不改变链表结构,所以代码仅供大家参考)
相遇时将链表断开,将相遇节点认为是新的头节点newhead,将相遇节点的前一个节点的指针域置空,由此可得到两个相交的单链表,返回相交链表第一个节点即为成环的第一个节点
代码如下:
二、复杂链表的深度拷贝
注意深度拷贝意味着一模一样,拷贝链表random要对应指向拷贝链表中的节点
思路:
1.遍历原链表,逐个拷贝并链接在对应节点后面,建立拷贝链表和原链表的联系
2.处理random,根据拷贝链表random指向必然在原链表random指向的后一个
3.将拷贝节点挨个摘下链接在一起,并恢复原链表(也可不恢复)
/**
* Definition for a Node.
* struct Node {
* int val;
* struct Node *next;
* struct Node *random;
* };
*/
typedef struct Node Node;
struct Node* copyRandomList(struct Node* head) {
Node* cur1=head,*cur2=head,*cur3=head;
//挨个拷贝并链接在对应节点后面
while(cur1)
{
Node* newnode=(Node*)malloc(sizeof(Node));
newnode->val=cur1->val;
newnode->next=cur1->next;
cur1->next=newnode;
cur1=cur1->next->next;
}
//处理random
while(cur2)
{
if(cur2->random==NULL)//random为空需要格外注意
{
cur2->next->random=NULL;
cur2=cur2->next->next;
}
else
{
cur2->next->random=cur2->random->next;
cur2=cur2->next->next;
}
}
//再将拷贝链表从原链表上摘下来尾插在一起
Node* copytail=NULL,*copyhead=NULL;
while(cur3)
{
Node* newnode=cur3->next;
Node* next=newnode->next;
if(copytail==NULL)
{
copyhead=copytail=newnode;
}
else{
copytail->next=newnode;
copytail=newnode;
copytail->next=NULL;
}
cur3->next=next;//恢复原链表
cur3=next;
}
return copyhead;//返回拷贝链表头节点
}