leetcode142.环形链表II——(C语言)

方法1:

直接方法:

  1. 先用快慢指针的方法判断是否成环
  2. slow==fast的节点看作是尾节点(不用置空),这时就可以将问题转化为求头指针为headfast->next的两个链表的交点了

1.如何用快慢指针的方法判断是否成环?
    //返回false为不成环,返回true为成环
    struct ListNode* slow = head;//慢指针为slow
    struct ListNode* fast = head;//快指针为fast
    if (head == NULL)//如果head==NULL,那么不可能成环,直接返回false
    {
        return false;
    }
    while (fast && fast->next)//因为快指针一次走两步,古fast和fast->next都不能为空
    //也因为fast快指针要比slow慢指针走的快,如果fast都不为空,slow也一定不为空,所以这里不需要加slow的判断语句
    {
        slow = slow->next;//慢指针一次走一步
        fast = fast->next->next;//快指针一次走两步
        if (slow == fast)//如果快慢指针相等,意味着他们在成环的链表中相遇了,也就是说链表成环
        {
            return true;
        }
    }
    return false;

 思考fast的速度一定要是slow的两倍吗?

        三倍和四倍可以吗?

 当slow走到mid时,fast进入圆环

slow进入圆环时,fast为圆环中的某一节点

假设圆环有C个节点,也就是圆环的周长为C

slow进入圆环时与fast相差X个节点

由于fastslow的运动方向是一样的,所以fastslow的相对距离为C-X

假设经过t次,slowfast会相遇

可得2t-t=C-X

t=C-X

所以fastslow总会在某一节点相遇

但如果fast不是slow的两倍,而是三倍四倍呢?

        我们假设fast是slow的3倍。

        3t-t=C-X,2t=C-X,C-X不一定会被2整除,也就是在C-X的距离上,fast和slow不一定会相遇,这时fast就会再次超过slow,如果fast依然领先slowX个节点,他们的相对距离依然为C-X,依然不会相遇,这就会陷入死循环。

        4,5,6……倍的情况也依然如此。

        所以fast的速度只能是slow的两倍

2.找两个链表交点的方法:

        在确保相交的情况下(判断是否相交只要看两链表的的尾节点的地址是否相同就可以了)

        之后分别遍历两个链表,计算他们的长度差,假如为num

        再让较长的链表先走num步,再与较短的链表同步遍历(这样两链表到达交点的步数就相同            了)

        接下来只要两链表的节点地址相同,就找到了他们的交点


对于环形链表,将fast==slow的节点看作是尾节点(不用置为NULL)

 这就可以看作求头指针为headfast->next的两个链表的交点问题了。

int s1 = 0;//记录List1的链表长度
int s2 = 0;//记录List2的链表长度
struct ListNode* List1 = head;
struct ListNode* List2 = fast->next;
while (List1 != fast)//把fast看作尾节点
{
    List1 = List1->next;
    s1++;
}
while (List2 != fast)
{
    List2 = List2->next;
    s2++;
}
List1 = head;//将List1看作是长链表,这里不知道head和fast->next谁长谁短,只是假设head为长链表
List2 = fast->next;//将List2看作是短链表
if (s1 < s2)//如果s2>s1说明fast->next才是长链表
{
    List1 = fast->next;
    List2 = head;
}
int num = s1 - s2 > 0 ? s1 - s2 : s2 - s1;//将s1-s2的绝对值记为num
while (num--)//先将长链表遍历num次
{
    List1 = List1->next;
}
while (List1 != List2)//共同遍历,如果相等则找到交点
{
    List1 = List1->next;
    List2 = List2->next;
}
return List1;
方法1完整代码如下:
struct ListNode* detectCycle(struct ListNode* head) {
    //方法1:
    struct ListNode* slow = head;//慢指针为slow
    struct ListNode* fast = head;//快指针为fast
    if (head == NULL)//如果head==NULL,那么不可能成环,直接返回NULL
    {
        return NULL;
    }
    while (fast && fast->next)//因为快指针一次走两步,古fast和fast->next都不能为空
    //也因为fast快指针要比slow慢指针走的快,如果fast都不为空,slow也一定不为空,所以这里不需要加slow的判断语句
    {
        slow = slow->next;//慢指针一次走一步
        fast = fast->next->next;//快指针一次走两步
        if (slow == fast)//如果快慢指针相等,意味着他们在成环的链表中相遇了,也就是说链表成环
        {
            int s1 = 0;//记录List1的链表长度
            int s2 = 0;//记录List2的链表长度
            struct ListNode* List1 = head;
            struct ListNode* List2 = fast->next;
            while (List1 != fast)//把fast看作尾节点
            {
                List1 = List1->next;
                s1++;
            }
            while (List2 != fast)
            {
                List2 = List2->next;
                s2++;
            }
            List1 = head;//将List1看作是长链表,这里不知道head和fast->next谁长谁短,只是假设head为长链表
            List2 = fast->next;//将List2看作是短链表
            if (s1 < s2)//如果s2>s1说明fast->next才是长链表
            {
                List1 = fast->next;
                List2 = head;
            }
            int num = s1 - s2 > 0 ? s1 - s2 : s2 - s1;//将s1-s2的绝对值记为num
            while (num--)//先将长链表遍历num次
            {
                List1 = List1->next;
            }
            while (List1 != List2)//共同遍历,如果相等则找到交点
            {
                List1 = List1->next;
                List2 = List2->next;
            }
            return List1;

        }
    }
    return NULL;
}

方法2:

方法二的代码很简单

但重要的是理解为什么要这样写

    struct ListNode* slow = head;//慢指针为slow
    struct ListNode* fast = head;//快指针为fast

Shead到交点位置的节点数,C为圆环的节点数也就是周长,X为交点到fastslow相遇的节点数

在这个过程中,slow的移动距离为S+Xfast的移动距离为S+X+N*CNfastslow相遇前fast绕圆环运动的圈数)

由于fast的速度是slow的两倍

可得

2*(S+X)=S+X+N*C
      S+X=N*C    
        S=N*C-X
        S=(N-1)*C+C-X

 前面的N-1圈是在slow进入圆环前手进行的,可以忽略

所以

S=C-X

也就是说以fasthead为头结点的链表到达交点的节点数是相同的

fasthead进行同步遍历,他们相等的地方就是交点所在的位置,也就是圆环的开始

方法2:完整代码如下:
struct ListNode* detectCycle(struct ListNode* head) { 
   struct ListNode* slow = head;//慢指针
    struct ListNode* fast = head;//快指针
    if (head == NULL)//如果head为NULL,不存在环形,直接返回NULL
    {
        return NULL;
    }
    while (fast && fast->next)//和前面的原理相同,因为快指针一次走两步,古fast和fast->next都不能为空
    //也因为fast快指针要比slow慢指针走的快,如果fast都不为空,slow也一定不为空,所以这里不需要加slow的判断语句
    {
        slow = slow->next;//一次一步
        fast = fast->next->next;//一次两步
        if (slow == fast)//相遇说明存在圆环
        {
            struct ListNode* meet = fast;//fast的地址赋值给meet
            while (meet != head)//相等时就是相交点,圆环的开始
            {
                meet = meet->next;
                head = head->next;
            }
            return meet;
        }
    }
    
    return NULL;
}

ps:如果对你有帮助的话麻烦给个免费的赞,求求你了。

pps:本人也是在学习中,如果有什么不对的地方欢迎在评论区指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值