经典问题---链表带环问题
最后一个节点的next指针,本来应该指向空指针,但是现在指向前面的节点(非NULL),这样就构成了带环链表
例子1:
判断链表是否带环(力扣)
解题代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head) {
struct ListNode*slow=head,*fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(fast==slow)
{
return true;
}
}
return false;
}
如果fast指针为NULL或者fast->next指针为NULL,就跳出循环,返回false。
毫无疑问,这道题的思路和代码都是非常简单的。
但是当我们思考得深一点时,slow进入环时,slow和fast指向同一个节点时返回true,那问题来了,两个指针一定会相遇吗?会不会错过呢?
2.原题是slow一次走一步,fast一次走两步。
如果fast一次走3步,4步,5步......N步呢?
所以就画图接着分析当N为奇数的情况
当值为-1时,说明fast已经超过了slow指针一步,设带环部分长为C,按fast追击slow的角度,则此时两指针相差C-1个距离。
这样就有回到了开始时的状态,不过起始距离不是N,而是C-1。
总结一下:(不一定对)
1.N为偶数,第一轮追击就会相遇。
2.N为奇数:第一轮追击错过,距离变为C-1。
a.C-1为偶数,第二轮就相遇。
b.C-1为奇数,这一轮最后距离还是C-1,进入循环,不会相遇。
那就需要一个等量关系来表示两者的关系了。还记得吗,fast的指针速度是slow的3倍,所以fast
走的距离也是slow的3倍。
当slow刚进入链表带环部分时,
slow=L
fast=L+X*C+C-N ,X为整数,X*C表示fast绕带环部分几圈了,最后多走C-N的距离。
3slow=fast
最后化简2L=(X+1)*C-N,对这个表达式简单的分析,等式的左边一定为偶数,N为奇数进入第二轮循环,按上面的分析C为偶数时就不会相遇,但是当C为偶数时,等式左边这个整体还为偶数吗?
显然一个偶数减去一个奇数为奇数,而2L为偶数,所以不存在C为偶数,同时N为奇数这种情况,即两快慢指针一定会相遇。
例子2:
随机链表的深拷贝(来自力扣)
ps:深拷贝的意思就是拷贝这个链表,链表中一个节点的val值,next指针的指向,随机指针的指向,都与原来给定的链表的内容完全相同,但是链表每个节点的地址与原来对应链表的节点的地址不同。(题中这么长的信息的意思就是这个意思)
首先思考,
1.用malloc函数开辟节点,然后赋值原链表的val值,next指针指向下一个新创建的节点。
如图:
或许普通单链表可以这样,但是这里面的random指针,你如何去找呢?
所以像这种直接malloc然后用next指针串起来是不行的,random指针的指向只有在原链表中才可以体现出来,所以应该思考如何将开辟的新节点与原链表扯上关系,既然不能直接将拷贝后的链表单独拿回来,那就将拷贝后的放到原链表去找两个链表之间的关系。
如图:将malloc后的节点插入到原节点的后面
这样就建立起了拷贝节点之间random指针之间的关系,最后再将链表恢复(将拷贝链表分离出来)
总结:
链表题要多多画图,多多分析,才能进步!!!