当前位置: 首页 > article >正文

第七期——环形链表2

题目

查看原题目请点击这里~

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

/**

 * Definition for singly-linked list.

 * struct ListNode {

 *     int val;

 *     struct ListNode *next;

 * };

 */

struct ListNode *detectCycle(struct ListNode *head) {

}

思路

在第六期中,我们已经介绍过,可以通过快慢指针来判断链表是否带环结构,具体方法是:

  1. 建立两个指针变量,一个是fast指针,另一个是slow指针,让着两个指针变量从链表头开始遍历链表,其中fast指针一次走两步,slow指针一次走一步
  2. 如果链表没有环结构,则fast指针最后一定会指向NULL
  3. 如果链表存在环状结构,则fast指针就会先进入环结构中,而slow指针会后进入环结构中,fast指针和slow指针在链表的环结构中做追击运动,最后fast指针一定会和slow指针相等

问题1

那么为什么fast指针一次走两步,slow指针一次走一步一定会相遇?它们会错过吗?

答:假设链表带环,两个指针最后都会进环,fast指针先进环,slow指针后进环。当慢指针刚好进环的时候,可能就和fast指针相遇了,最坏的情况就是两个指针之间的距离刚好是环的长度,此时fast指针和slow指针每移动一次,fast指针和slow指针之间的距离就缩小一步,不会出现刚好套圈的情况,因此:fast指针一次走两步,slow指针一次走一步,它们两个一定会相遇的,即相等。

问题2

fast指针一次走3步、4步、......n步行吗?

答:不行

我们假设slow指针刚好进环的时候,fast指针和slow指针之间的距离为N,环的周长为C

当N为零的时候,fast指针和slow指针恰好相等。

如果fast指针一次走3步,fast指针和slow指针之间的距离一次缩小2步,如果N为偶数,那么fast指针和slow指针的确可以相遇;但是如果N为奇数,fast指针和slow指针会恰好错过,这时fast指针和slow指针之间的新距离为N'=C-1,若C-1为偶数,那么fast指针和slow指针会在第二圈相遇,如果C-1为奇数,那么fast指针和slow指针永远不会相遇。

如果fast指针一次走4步,那么fast指针和slow指针之间的距离一次就缩小3步,N又要分为三种情况讨论,在所有情况中,一定存在fast指针和slow指针不相遇的情况。

当我们已经判断出链表带有环结构,那我们如何能得到进入环的第一个节点呢?

我们设从链表的第一个节点到进入环的第一个节点长度为L,slow指针在环中走的步数为X

fast指针走的距离就是L+n*C+X,slow指针走的距离就是L+X,又因为fast指针走的距离是slow指针走的距离的2倍,所以2*(L+n*C+X)=L+X,最后得到式子:L=(n-1)*C+C-x,于是我们知道,如果我们在相遇点建立一个指针meet,在起始点建立一个指针start,这两个指针分别一次走一步,一定在入口点相遇!

代码1(C语言版)

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

        }
    }
    return NULL;

}

代码2(C语言版)

我们在第六期介绍了如何找到相交链表的第一个公共节点的方法,所以当我们找到带环链表的相遇点之后,我们也可以把链表的环结构断开,将这个链表分为两个有公共节点的链表,而这个公共节点就是我们要找的入口点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode* tailA=headA;
    struct ListNode* tailB=headB;
    int lenA=0;
    int lenB=0;
    while(tailA)
    {
        tailA=tailA->next;
        lenA++;
    }
    while(tailB)
    {
        tailB=tailB->next;
        lenB++;
    }
    if(tailA!=tailB)
        return NULL;
    int gap=abs(lenA-lenB);
    struct ListNode* longlist=headA,*shortlist=headB;
    if(lenA<lenB)
    {
       longlist=headB;
       shortlist=headA;
    }
    while(gap--)
    {
        longlist=longlist->next;
    }
    while(longlist!=shortlist)
    {
        longlist=longlist->next;
        shortlist=shortlist->next;
    }
    return shortlist;
}
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* slow,*fast;
    slow=fast=head;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
        if(slow==fast)
        {
            struct ListNode* meet=slow;
            struct ListNode* lt1=head;
            struct ListNode* lt2=meet->next;
            meet->next=NULL;
            return getIntersectionNode(lt1,lt2);

        }
    }
    return NULL;

}

持续更新,下期见


http://www.kler.cn/a/558786.html

相关文章:

  • MySQL 如何使用EXPLAIN工具优化SQL
  • devops-Jenkins一键部署多台实例
  • 2025年02月21日Github流行趋势
  • 把 vscode 伪装成 goland
  • MinIO对象存储在Windows中的部署方法
  • go 语言中的线程池
  • 项目8:信用违约预测-集成学习
  • Debian软件包重构
  • PAT甲级 1103 Integer Factorization
  • Android Loader机制解析
  • elf_loader:一个使用Rust编写的ELF加载器
  • RabbitMQ学习—day6—死信队列与延迟队列
  • 网络IP跳动问题解决详
  • flink operator v1.10部署flink v1.19.2
  • 前后端分离系统架构:基于Spring Boot的最佳实践
  • Python数据结构深度探索:树的构建与遍历
  • 跟据spring boot版本,查看对应的tomcat,并查看可支持的tomcat的版本范围
  • 高速PCB电源层
  • 跟着 Lua 5.1 官方参考文档学习 Lua (8)
  • MyBatis框架七:缓存