美文网首页Python
python算法-014找出环的出口点(接013)

python算法-014找出环的出口点(接013)

作者: DKider | 来源:发表于2019-03-12 23:26 被阅读38次

你今天晚了!
你看看现在几点了?

你的头发 。。

感谢425以及身边的同学,给我的动力,让我完成了今天的任务。今天就把找环出口给写了吧!


题目:给一个长度较大的链表,判断其是否有环存在,并找出环的出口点。例如:

H->1->3->4->6->7->0->2->4
1-          ^           |
            |           7
2-          |           |
            |           v
3-          7<-0<-2<-4<-3

这里说下为什么要找环的出口:因为如果链表有环,在遍历链表或者释放链表所占空间时,会很麻烦,如果我们知道了环的出口,就会变得很简单。

013的两种方法都可以实现判断是否有环,接下来就是找到环的出口,接着看着个字符图:

H->1->3->4->6->7->0->2->4
1-          ^           |
            |           7
2-          |           |
            |           v
3-          7<-0<-2<-4<-3
图中的节点6就是我们要找的出口。根据我们找环方式的不同,判断其出口就有略微的差别:

用蛮力法的话,在判断其是否相等时,就可以得到,相等的那个点就是我们要找的出口,返回这个节点就可以了。这代码简单,013代码加一行return 就能完成就不写了。

如果用快慢指针法,就有点差别。比如下面的情况:

         f4
   f1    f2
      s6 s7
   s1 s2 s3        s代表slow,f代表fast,
   |  |  |         边上的数字代表遍历的次数
   V  V  V   
                   根据要求:每次遍历作比较
H->1->2->3         也就是遍历次数相同的作比较
      ^  |         我把s放在内层了,从图上来看,
      |  V         是在第5次遍历完成的判断,此时
      5<-4 <—s4   
 
      ^
      |
      s5
      f3
      f5

由此看来,这不是有点差别,差别还不小了。这还只是5个节点,那如果更大怎么办?
当有环时:
slow走的比fast慢,所以slow在与fast相遇时,slow一定没有遍历完链表,而fast已经进入环的循环,设slow遍历过了s个节点,则fast遍历了2s个。设fast在环里面转了n圈了,n>=1。设环的长度为r个节点,出口是第a个节点,当slow与fast相遇时的节点是第x个节点,链表长度为L。接下来就是数学题了,怎样解x?
高中数学学的不错的我,一眼看出了答案呢,你呢?
我大学学会了吹NB。。。
以下的推导过程还要感谢我的室友NJF同学:
fast走的步数还等于s+n*r,所以有:
2s=s+n*r=>s=n*r
又:a+x=nr
a+x=(n-1)*r+r=(n-1)*r+L-a
a=(n-1)*r+(L-a-x)
L-a-x为相遇点到环入口的距离
a=(n-1)*r+(L-a-x)这个式子的意义就是:链表头到环入口的距离=(n-1)*环长+相遇点到环入口的距离。
所以从链表头设一指针A,相遇点设一指针B,同时遍历,每次一步,必定相遇,且相遇点为入口点!
这太神奇了!感谢JF。
代码:

import random
import time
class LNode:
    def __init__(self,arg):
        self.data=arg
        self.next=None

"""
题目描述:
链表中的某个节点的next域指向的是链表中在他之前的某一个节点,这样在链表尾部形成了环的结构。如何判断链表中是否有环的结构
要求:
方法:HashSet蛮力法
"""
def constructLinkwithAnnulation(x):
    i = 1
    head = LNode(None)
    tmp = None
    cur = head
    while i <= x:
        n = random.randint(1, 9)
        tmp = LNode(n)
        cur.next = tmp
        cur = tmp
        i += 1
    # 上面的部分 就是之前我们用的构造链表的方法
    #这里我是设置了二分之一的概率产生有环的链表
    if random.randint(0,1)==0:
        m = random.randint(1, x - 1)  # 用于选择出环的入口是哪个
        Annulation=head.next # 用于遍历到选定的节点,就是出环的入口
        while m>0:
            Annulation=Annulation.next
            m-=1
        # 因为前面构造链表时就用了cur,最后他指向了最后一个节点
        cur.next=Annulation #这里将尾结点的next域指向环入口
    return head
def judgeAnnulation(head):
    #判断链表是否为空
    if head.next is None or head.next is None:
        return None
    #都指向第一个节点
    slow=head.next
    fast=head.next
    #开始遍历
    while fast is not None:
        #这里注意一定先操作再判断,因为如果先判断的话,
        # 第一个节点就是相等的
        slow = slow.next
        fast = fast.next.next
        if slow==fast:
            #这里直接判断两个节点是否完全相等,返回节点
            return slow
    return False

def Finddoor(head,meetNode):
    A=head.next
    B=meetNode
    while A is not B:
        A=A.next
        B=B.next
    return B

if __name__ == '__main__':
    start = time.time()
    head = constructLinkwithAnnulation(1000000)
    end = time.time()
    print("创建用时:", end - start)
    cur = head.next
    start1=time.time()
    buer=judgeAnnulation(head)
    end1=time.time()
    print("判断用时:",end1-start1)

    if buer == False:
        print("head中没环")
    elif buer==None:
        print("head为空")
    else:
        print("head中有环,出口为:")
        print(Finddoor(head,buer).data)
1000000 1000000

感谢大家对我的监督,我会努力的,不管掉不掉头发!

加油!

相关文章

网友评论

    本文标题:python算法-014找出环的出口点(接013)

    本文链接:https://www.haomeiwen.com/subject/cxtapqtx.html