LeetCode第141题----环形链表I和LeetCode142题----环形链表II

一. 环形链表I

题目描述:

给定一个链表,判断链表中是否有环。

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

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

在这里插入图片描述
在这里插入图片描述
解题的代码量很少,但是并不是说就很简单,这个题的实现思路也不是很困难,但是如果给你增加一问:
你能否证明快指针是如何追上慢指针的?

你假设的慢指针一次走一步,快指针一次为什么走两步?走三步或者四步不行吗?走n步不行吗?
答案是:不行 ,你假设slow每次走一步,然而fast每次走三步,那么他们都进入环内时相差X步,那么剩下每走一步他们之间就会缩小2步,剩余的为X-2步,当X为奇数时还好,但是为偶数时他们之间刚好就会错开遇不到,剩下的fast走四步,n步都是类似的。最好的解题思路就是fast走两步,因为这样他们每次之间的距离就缩小1步,绝对不会错开,一定会想遇到,并且慢指针也绝不会走的长度超过环的周长,就会被快指针追上。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
*/
bool hasCycle(struct ListNode *head) {
    
    
    struct ListNode* fast = head,*slow = head;
    //因为你并不能确定这个链表一定是带环的,所以循环的条件还是要按照正常的思路来写的
    while(fast && fast->next)
    {
    
    
        //慢指针走的慢,快指针一次走两步走的快,如果有环,那么总会达到快指针追上了慢指针
        slow = slow->next;
        fast = fast->next->next;
        //他们都进入了环里,快指针追上了慢指针
        if(slow ==fast)
            return true;
    }
    return false;   
}

二.环形链表II

题目描述:
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
在这里插入图片描述
在这里插入图片描述
一共有两种解题思路:
①写起来麻烦,但是理解很简单:slow指针走一步,fast指针走两步,那么总会在环里相遇,那么那个点就是相遇点,然后我让这个环从这个相交点断开,一个是最开始的头,另一个你设置一个新的头(断开点的下一个位置),那么你让两个链表走,就会发现有相交的点,就转换为了原来做过题的类型了(链表的相交)

②写起来很简单但是理解起来很麻烦,并且还需要公式的证明。
错误的解题思路:慢指针不可能在环里走的距离超过环的周长,所以慢指针走的长度一定为L+X的距离,快指针走的距离是L+X+C(其中L是进入链表前的距离,X表示从进入环开始到相遇之间的差值,C表示这个环的周长) 2*(L+X) = L+X+C,这样子想就过于片面。
在这里插入图片描述
当L远远大于C的时候,你就会发现你的fast很早就进入环内了,但是你的slow还是在L上面走,那么你的fast走了可就不止是比slow多一个C了。
正确的解法:快指针:L + NC + X 然后用这个公式去推最终的结果 2(L+X)= L + N*C + X 结论L=NC-X,你会发现上面错误的答案只不过是正确答案的一种情况而已,后面的步骤和上面第一种的解题思路一样,定义两个头,然后他们就会在L的末尾处相遇 ,那个就是相交点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) {
    
    
    struct ListNode* slow = head,* fast = head;
    while(fast && fast->next)
    {
    
    
        //这一步是在最后进行测试用例不过的时候返回来判断发现的一个问题,如果你把if判断放在前面,就会一开始的快慢指针就是相等的,那么你一进入这个循环的时候就直接break了
            slow = slow->next;
            fast = fast->next->next; 
            //所以这里选择先进行走一步,然后判断,不然就会直接跳出来了
            if(slow == fast)
                break;
    }
    //上面的循环跳出来有两种可能,第一如果链表是带环的,那么一定以break跳出来,那个时候就找到了快慢指针的相遇点,第二种就是都遍历完了,fast都走到NULL的时候依旧没有找到两个相遇的点,说明这个链表是不带环的。
    //这段代码表示这个链表不带回环,当为奇数个的时候fast为NULL,偶数个的时候fast->next为NULL,但是不管哪种情况出现,都可以表示这是一个没有回环的链表
    if(fast == NULL || fast->next == NULL)
        return NULL;
    struct ListNode * meet = fast;
    while(head != meet)
    {
    
    
        head = head->next;
        meet = meet->next;
    }
    return meet;
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/MEANSWER/article/details/111866939
今日推荐