链表OJ题之环形链表

  • 带环链表:尾节点的next指向链表中的任意点(甚至可能指向它自己)

思考 

接下来有几个问题需要我们来思考一下:

Q1

slow一次走1步,fast一次走2步,他们一定会相遇吗?(slow在走满一圈之前)

假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。如果是最差情况:两个指针之间的距离刚好就是环的长度,那么当慢指针刚进环时,就和快指针相遇了。若两者相距N,如图所示,此时,两个指针每移动一次,之间距离缩小一步,因为是整数,所以总会缩小到0。

所以它们一定会相遇! 

Q2

slow一次走1步,fast一次走3步,他们一定会相遇吗?

  • 设N是slow开始入环,fast的最初开始追击的距离,C是环的长度。 
  • 如果N是偶数第一轮就直接追上了。
  • 如果N是奇数,C是奇数,C-1是偶数,第二轮也追上了。
  • 如果N是奇数,C是偶数,C-1是奇数,永远追不上。

 

若第一轮没有追上,那么它们之间就差了-1,fast在前面,那么我们可以看C-1的奇偶,如果C-1是偶数,那么

 而如果C-1是个奇数,那么

请独立思考 

如果N是奇数,C是偶数,那么就追不上。这个条件是否成立?请证明!

其实这个条件是不存在的,不可能会出现这种情况!

 经过分析我们可以知道,当N是奇数,C是偶数的时候,这个式子不成立,所以这种情况一定不存在!所以 slow一次走1步,fast一次走3步,他们一定会相遇!

Q3

slow一次走n步,fast一次走m步,他们一定会相遇吗?(m>n>1) 

这个和Q2有相似之处。

  • 如果  N%(m-n) == 0 第一圈就直接追上了。
  • 如果  N%(m-n) == x 第一圈也追不上了。
  • 如果  (C-x)%(m-n) == 0 第二圈就追上了。
  • 如果  (C-x)%(m-n)  != 0   永远追不上了。
     

Q4

请证明:

让一个指针slow从链表起始位置开始遍历链表,同时让一个指针fast从判环时相遇点的位置开始绕环运行,注意slow每次走1步,fast每次走1步,最终肯定会在入口点的位置相遇。 

速度是二倍关系,那么相同时间里,fast的路程也是slow路程的2倍 

  

得证。

同时这个思路也可以用在Q2的独立思考题里。

通过这个结论,我们就可以找到入口点了!!

 

例题

环形链表1

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。 

 

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

思路:快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表起始位置开始往后走,如果链表带环则一定会在环中相遇,否则快指针率先走到链表的末尾。 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) 
{
    struct ListNode *faster=head;
    struct ListNode *slow=head;
    //一起走faster走2步 slow走1步--保持相对速度
    while(faster && slow && faster->next)
    {
        faster=faster->next->next;
        slow=slow->next;
        if(slow == faster)
        {
            return true;
        }
    }
    return false;
}

 环形链表2

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

思路:用Q4的结论: 

让一个指针slow从链表起始位置开始遍历链表,同时让一个指针fast从判环时相遇点的位置开始绕环运行,注意slow每次走1步,fast每次走1步,最终肯定会在入口点的位置相遇。 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode *slow=head;
    struct ListNode *fast=head;
    struct ListNode *meet=NULL;
    while(fast && fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast == slow)
        {
            slow=head;
            while(fast != slow)
            {
                slow=slow->next;
                fast=fast->next;
            }
            return fast;
        }
    }
    return NULL;
}

环形链表到相交链表的转化 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值