142. 环形链表 II

题目要求:

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。

思想:

第一种方法:
理解起来非常容易,但是代码写起来非常复杂,需要考虑到各种各样的情况。
常规情况
就是利用快慢指针思想,slow指针走一步,fast指针走两步,当其在链表循环体内一结点相遇时,将指向其的结点的next置为空,即prev->next=NULL。其指向的下一个结点作为另一个链表的头部,与head组成的另一个链表,两个链表相交于一个结点,且两个链表最后的尾部就是上述快慢指针相交结点的前一个结点,即prev。具体如图:
在这里插入图片描述
在这里插入图片描述
特殊情况:
1、将头节点作为循环点的起点,恰好快慢指针也相较于头结点。

[1,2]
0

针对此种情况的解决办法:
只需在检测快慢指针相遇后是否等于头结点即可,若等于头节点,则返回头节点。
####################################################################
2、
(1)、在尾结点进行循环时,指向的是自身,此时快慢指针的相交结点也恰好是尾结点,即

[-1,-7,7,-4,19,6,-9,-5,-2,-5]
9

在这里插入图片描述
(2)、在环中的某个位置相交,且此时快慢指针也恰好在此处相遇,

[-21,10,17,8,4,26,5,35,33,-7,-16,27,-12,6,29,-12,5,9,20,14,14,2,13,-24,21,23,-21,5]
24

在这里插入图片描述
针对此种情况的解决办法:
在快慢指针相遇之后,将相遇点之前的结点地址由prev指向,定义prev->next=NULL。在此处再定义一个结构体变量copyMeethead指向快慢指针相遇点slow指向的下一个结点Meethead指向的地址,然后建立一个循环向后遍历,先判断是否能遇见slow,假如能遇见,则slow对应的结点就是链表里循环体的起点,此时slow指向的结点地址是与原链表不相连的,则重新将prev->next=slow,返回此时slow的值;假如没能遇见,每次copyMeethead向后走一步,直至为空,循环停止。
其实自己画张图去分析分析,就知道在快慢指针相遇后,将相遇点前一个结点指向的next置为空,其下一个结点作为另一个链表的开头,这道题目也就转化成了寻找两个链表的公共结点,此时从头结点遍历一遍直到为空,其实是遇不见slow指向的结点地址。
假如此时在新开辟的以Meethead为头结点链表在向后遍历过程中,假如遇见了slow指向的结点地址,那么就一定能够判定此处就是环起点的结点。
###################################################################
其实我本人说的可能有点没有将太清楚,还请你们对着代码,与对应情况的所出现的情形,进行一步一步画图操作,这样就比光看我的解释清楚很多了。

看如下代码的注释,在对应情况的代码解法下好好理解。

####################################################################
第二种方法:
就是理解起来不是那么的容易,但是一旦理解了之后,代码书写上会非常简单。

这是当L与C相差不大的时候出现的情况。
在这里插入图片描述
最正确的解释应该是:
假如循环体的周长远远小于头结点到循环体起点结点的距离,也即是当slow走进循环体内与fast相遇时,fast已经走了很多圈了。
在这里插入图片描述
因此便有了第二种解法,根据代码与上述图解,自己画图理解一下,这样有助于自己理解这幅图以及如下写的代码。

代码实现:

第一种方法:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) {

    if(head==NULL)
    {
        return NULL;
    }
    if(head->next == NULL)
    {
        return NULL;
    }
    //1、先去找环中的相遇点
    struct ListNode *fast = head;
    struct ListNode *slow = head;
    struct ListNode *prev = NULL;

    while(fast && fast->next)
    {
        prev = slow;
        slow = slow->next;
        fast = fast->next->next;
        if(fast == slow)
        {
            break;
        }
    }

    //2、去除掉这个相遇点,将其指向的下一个结点作为另一个链表的头
    //此举将其转化为寻找两个链表的公共结点
    
    // 特殊情况的情况1
    if(slow == head)
    {
        return head;
    }
    struct ListNode *Meethead = slow->next;
    struct ListNode *prev1 = NULL;
    struct ListNode *copyMeethead = Meethead;
    prev->next = NULL;
    
    // 特殊情况的情况2
    while(copyMeethead)
    {
        if(copyMeethead == slow)
        {
            prev->next = slow;
            return slow;
        }
        copyMeethead = copyMeethead->next;
    }

    //3、找寻两个链表的公共结点
    struct ListNode* Alen = head;
    struct ListNode* Blen = Meethead;
    int la = 0;
    int lb = 0;

    //(1)、计算两个链表的长度
    while(Alen)
    {
        la++;
        Alen = Alen->next;
    }
    while(Blen)
    {
        lb++;
        Blen = Blen->next;
    }

    //(2)、比较两个链表的长度,将长的链表走两个链表长度的差值
    struct ListNode* longList = head;
    struct ListNode* shortList = Meethead;
    if(la<lb)
    {
        longList = Meethead;
        shortList = head;
    }
    int gap = abs(la-lb);
    while(gap--)
    {
        longList = longList->next;
    }
    //(3)、比较两个链表指向的地址是否相同,如相同,则为公共结点
    while(longList)
    {
        if(longList == shortList)
        {
            return longList;
        }
        longList = longList->next;
        shortList = shortList->next;
    }

    return NULL;
}

第二种方法:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode *fast = head;
    struct ListNode *slow = head;

    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(fast == slow)
        {
            break;
        }
    }

    if(fast == NULL || fast->next == NULL)
    {
        return NULL;
    }

    struct ListNode *Meet = fast;
    while(head != Meet)
    {
        Meet = Meet->next;
        head = head->next;
    }
    return head;
}

总结:

以上就是我针对这道题目的一些总结,可能写的不好或者有一定的错误,请大家见谅。如果里面的有些地方写的错误或者还可以在完善一下,希望你们在评论区可以给我提出你们的建议。在此谢谢大家了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值