[面试专题05]Python全栈日记-判断链表是否存在环

在这里插入图片描述
Python的面试题,之前的班考试的时候发的,3道数据结构的题,冒泡之前在学数据结构的时候说过,二叉树之后会讲的,今天说说检查一个单链表是否有环和如何确定环的位置。在这里插入图片描述

首先如何确定有环?
我们给链表设置一个指针,当这个指针一直往下走,如果指针最后等于none,则单链表无环
在这里插入图片描述
如果指针一直走,不会停下,那么就说明有环。
在这里插入图片描述

确定有环很简单,那如何确定入口呢?也就是环在哪里开始。

我们给单链表的头设置两个指针,一个为slow一个为fast。
slow每次前进1格,slow=slow.next
fast每次前进2格,fast=fast.next.next
这是一个数学问题,如果有环那么fast指针一定会追赶上slow指针

我们假设从head到入口i的长度为a
假设他们在p点相遇
相遇是距离入口为x
环的周长为r
在这里插入图片描述
接下来就是通过数学公式的推导来验证a与·slow和fast移动时的关系。

警告:接下来的内容纯为数学推导,如果用正常逻辑进行思考是想不明白为啥会这样的,而且我感觉只在fast每次移动距离是slow两倍下成立,我们只需要通过数学推导找出如何求a就行了。

首先:
当slow和fast在p点相遇,因为fast速度是slow的两倍,所以相遇时fast走过的路程也是slow的两倍,所以我们设slow走过的路程为S,fast走过的路程为2S

Fast和slow相遇时,fast肯定在环中不断转圈,假设转了n圈,
因为他们经过的a与x相同,所以他们的路程差就为fast多转的这几圈
所以:2S – S = nr
又因为:2S – S = S
所以:S = nr 、2S = 2nr

又因为fast走过的路程也可以写为:
2S = a + x + nr
2S = 2nr
所以:
a + x + nr = 2nr

推出:
a = nr -x = (n-1+1)r – x = (n-1)r +( r – x)

所以最后推导出
a = (n-1)r +( r – x)

这个式子中:
(r – x) 在图中就是紫色的这段长度
(n-1)r:就是在环中转圈
在这里插入图片描述
所以就是说,当我们fast和slow相遇在p后,如果把slow扔在head处,
让slow每次还是前进一格( slow = slow.next )
然后fast从相遇点p开始每次前进一格( fast = fast.next )
那么他们一定会在循环入口i点相遇
在这里插入图片描述
因为a = (n-1)r +( r – x)
a 代表slow从head移动到入口i的距离,
*a =(n-1)r +( r – x)*代表fast从相遇点p出发不论在环中绕多少圈,最后都会在i点与slow相遇。
所以通过slow再次与fast相遇时走过的距离就能直到循环入口i在何处。

过程就是这样,你要去想其中的逻辑就会钻牛角尖,因为这个纯为数学推导,并没有为啥。
接下来通过代码实现:

class Node(object):   #链表指针和内容
    def __init__(self,item):
        self.item = item   #链表每个结点的内容
        self.next = None   #无环的单链表的最后节点应该指向空

#我们先创建一个又环的链表,这里就不用类了,面试的时候如果没要求也可以这样
node1 = Node(1)
node2 = Node(2)
node3 = Node(3)
node4 = Node(4)
node5 = Node(5)
node6 = Node(6)

node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
node5.next = node6
node6.next = node3  #最后一个节点指向了节点3,创造出环


def findloop(head):   #判断是否有环的函数并返回环入口
    slow = head   #先把slow和fast都放在head位置
    fast = head
    loopExist = False   #定义一个标记,标记是否存在环
    if head == None:   #判断链表是否为空
        return False

    while fast.next!=None and fast.next.next != None: #让fast和slow开始走,如果fast一直不为空说明有环
        fast = fast.next.next  #fast每次前进2格
        slow = slow.next       #slow每次前进1格
        if slow == fast:    #如果slow和fast的所在节点的值相等就说明相遇了
            loopExist=True  #这是标记变为true代表有环
            break           #while循环结束,继续向下

    if loopExist ==  True:  #如果有环
        slow = head         #把slow放在链表head处
        while slow != fast: #当slow和fast不相等的时候,让各自前进一步,当相等时不成立,跳出while循环
            slow = slow.next
            fast = fast.next
        return slow.item    #返回相等时slow的节点值
    return False  #和最外面的while是一对,如果那个循环结束就说明fast的next或者next.next为空,说明没环,返回false

if findloop(node1):    #设置第一个节点为head,如果返回为true
    print('存在环')
    print(findloop(node1))
else:              #如果返回值为false
    print("不存在环结构")

结果:

存在环
3

我修改一下我的链表,让他没环,然后试试能否判断出来:

#我们先创建一个又环的链表,这里就不用类了,面试的时候如果没要求也可以这样
node1 = Node(1)
node2 = Node(2)
node3 = Node(3)
node4 = Node(4)
node5 = Node(5)
node6 = Node(6)

node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
node5.next = node6
#node6.next = node3  #最后一个节点指向了节点3,创造出环

结果:

不存在环结构

猜你喜欢

转载自blog.csdn.net/weixin_39561473/article/details/85228321
今日推荐