判断单链表是否有环, 找到入口点, 环的长度....

给定一个单链表,判断其中是否有环,已经是一个比较老同时也是比较经典的问题,在网上搜集了一些资料,然后总结一下大概可以涉及到的问题,以及相应的解法。

 

首先,关于单链表中的环,一般涉及到一下问题:

1.给一个单链表,判断其中是否有环的存在;

2.如果存在环,找出环的入口点;

3.如果存在环,求出环上节点的个数;

4.如果存在环,求出链表的长度;

5.如果存在环,求出环上距离任意一个节点最远的点(对面节点);

 

下面,我将针对上面这些问题一一给出解释和相应的代码。

1.判断时候有环(链表头指针为head)

对于这个问题我们可以采用“快慢指针”的方法。就是有两个指针fast和slow,开始的时候两个指针都指向链表头head,然后在每一步

操作中slow向前走一步即:slow = slow->next,而fast每一步向前两步即:fast = fast->next->next。

由于fast要比slow移动的快,如果有环,fast一定会先进入环,而slow后进入环。当两个指针都进入环之后,经过一定步的操作之后

二者一定能够在环上相遇,并且此时slow还没有绕环一圈,也就是说一定是在slow走完第一圈之前相遇。证明可以看下图:

当slow刚进入环时每个指针可能处于上面的情况,接下来slow和fast分别向前走即:

1

2

3

4

5

if (slow != NULL && fast->next != NULL) 

         slow = slow -> next ; 

         fast = fast -> next -> next ; 

  也就是说,slow每次向前走一步,fast向前追了两步,因此每一步操作后fast到slow的距离缩短了1步,这样继续下去就会使得两者之间的距离逐渐缩小:...、5、4、3、2、1、0 -> 相遇。又因为在同一个环中fast和slow之间的距离不会大于换的长度,因此

到二者相遇的时候slow一定还没有走完一周(或者正好走完以后,这种情况出现在开始的时候fast和slow都在环的入口处)。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

typedef struct node{ 

    char data ; 

    node * next ; 

}Node; 

bool exitLoop(Node *head) 

    Node *fast, *slow ; 

    slow = fast = head ; 

   

    while (slow != NULL && fast -> next != NULL) 

    

        slow = slow -> next ; 

        fast = fast -> next -> next ; 

        if (slow == fast) 

            return true 

    

    return false 

  下面看问题2,找出环的入口点: 

  我结合着下图讲解一下:

a 代表head到入口点的距离, x表示第一次相遇时, slow走的距离, r表示环的长度.

思路1 :  (a+Nr)%r==a,从fast和slow开始相遇的点开始, 此时在用一个新指针head从头开始, slow也开始走, 当他们相遇时, slow正好走了一个整圈,  a+r,  此时head也走了a步, head和slow相遇的地方就是入口点.

思路2 : 当slow和fast第一个相遇的时候, slow走了a+x距离, fast走了a+r+x距离, 由于fast一直是slow的2倍, 2*slow=fast 所以: 2*(a+x) = a+r+x; 计算之后可得 a=r-x;  这也可以说明, 当第一次相遇的时候, 我们用一个新指针从头开始, 那么一定会和slow在入口点相遇.

到此第二个问题也已经解决。

下面给出示意性的简单代码(没有测试但是应该没有问题):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

Node* findLoopStart(Node *head) 

    Node *fast, *slow ; 

    slow = fast = head ; 

   

    while (slow != NULL && fast -> next != NULL) 

    

        slow = slow -> next ; 

        fast = fast -> next -> next ; 

        if (slow == fast) break 

    

    if (slow == NULL || fast -> next == NULL) return NULL ; //没有环,返回NULL值 

   

    Node * ptr1 = head ; //链表开始点 

    Node * ptr2 = slow ; //相遇点 

    while (ptr1 != ptr2)  

    

        ptr1 = ptr1 -> next ; 

        ptr2 = ptr2 -> next ; 

    

    return ptr1 ; //找到入口点 

  

第3个问题,如果存在环,求环上节点的个数:

对于这个问题,我这里有两个思路(肯定还有其它跟好的办法):

思路1:记录下相遇节点存入临时变量tempPtr,然后让slow(或者fast,都一样)继续向前走slow = slow -> next;一直到slow == tempPtr; 此时经过的步数就是环上节点的个数;

思路2: 从相遇点开始slow和fast继续按照原来的方式向前走slow = slow -> next; fast = fast -> next -> next;直到二者再次相遇,此时slow经过的步数就是环上节点的个数 。slow走了1圈, fast走了2圈.

 

第一种思路很简单,其实就是一次遍历链表的环,从而统计出点的个数,没有什么可以详细解释的了。

对于第二种思路,我们可以这样想,结合上面的分析,fast和slow没一次操作都会使得两者之间的距离较少1。我们可以把两者相遇的时候看做两者之间的距离正好是整个环的长度r。因此,当再次相遇的时候所经过的步数正好是环上节点的数目。

由于这两种思路都比较简单,代码也很容易实现,这里就不给出了。

 

问题4是如果存在环,求出链表的长度:

到这里,问题已经简单的多了,因为我们在问题1、2、3中已经做得足够的”准备工作“。

我们可以这样求出整个链表的长度:

链表长度L = 起点到入口点的距离 + 环的长度r;

已经知道了起点和入口点的位置,那么两者之间的距离很好求了吧!环的长度也已经知道了,因此该问题也就迎刃而解了!

 

问题5是,求出环上距离任意一个节点最远的点(对面节点)

如下图所示,点1和4、点2和5、点3和6分别互为”对面节点“ ,也就是换上最远的点,我们的要求是怎么求出换上任意一个点的最远点。

对于换上任意的一个点ptr0, 我们要找到它的”对面点“,可以这样思考:同样使用上面的快慢指针的方法,让slow和fast都指向ptr0,每一步都执行与上面相同的操作(slow每次跳一步,fast每次跳两步),

当fast = ptr0(圈内是偶数个元素)或者fast = prt0->next(圈内是奇数个元素)的时候slow所指向的节点就是ptr0的”对面节点“。

为什么是这样呢?我们可以这样分析:

如上图,我们想像一下,把环从ptro处展开,展开后可以是无限长的(如上在6后重复前面的内容)如上图。

现在问题就简单了,由于slow移动的距离永远是fast的一半,因此当fast遍历玩整个环长度r个节点的时候slow正好遍历了r/2个节点,

也就是说,此时slow正好指向距离ptr0最远的点。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下:首先,每份源码都提供了详细的运行境和依赖说明,确保用户能够轻松搭建起开发境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值