zoukankan      html  css  js  c++  java
  • 检测一个较大的单链表是否有环

    【阿里巴巴笔试题】

    题目描述:

    单链表有环指的是单链表中某个结点的next域指向的是链表中它之前的某一个结点,这样在链表的尾部形成一个环形结构。如何判断单链表是否有环?如何找到环的入口点?、

    方法一:哈希集合法
    思路:可以定义一个空集合,接着从头遍历这个单链表,如果当前结点的next不在集合中就继续往后走,如果在集合中,当前结点的next就是环的入口。

    def check(link: SingleLink):
        s = set()  # 构造一个空的set
        p = link.head
        if p is None:  # 空链表直接返回False
            return False
        s.add(p)  # 不空的话把第一个结点加入到s中
        while p.next is not None:  # 如果当前结点的next不是空的话
            if p.next in s:  # 当前结点的下一个结点在集合中
                return p.next.data  # 入口就是当前结点的下一个结点
            s.add(p.next)  # 不在就把当前结点加入到集合中
            p = p.next
        return False  # 如果一直到结尾都没有,那就不是环,返回False
    

    方法二:快慢指针法
    我们设置一个慢指针slow和一个快指针fast,慢指针每次向后走一步,快指针每次走两步,因为有环,所以快指针早晚会追上慢指针的。如下图(自己手画的):
    在这里插入图片描述
    我们可以清楚的看到,对于这个有环的单链表,快指针会走到结尾7之后再沿着环绕回来最终在5结点的位置追上慢结点,此时我们的fast和slow指针都指向这个5结点;
    紧接着我们用一个新的变量a指向链表的开头,然后让a和fast同时往后走,每次都走一步,最终会在4结点相遇,这个4结点就是链表的环的入口。这样我们就找到了环形链表的入口了。
    这样简单理解很容易,其实这个思路后面是由Floyd算法在做支撑的,Floyd判圈算法也称为龟兔赛跑算法,可以判断链表迭代函数有限状态机是否有环,如果有就可以找出环的起点和大小,时间复杂度是O(n),空间复杂度是O(1)。
    扩展链接:Floyd算法
    本题代码实现:

    # 快慢指针法
    def check(link: SingleLink):
        slow = fast = link.head  # 快慢指针都指向 第一个结点
        circle = False  # 假设没有环
        while slow and fast and fast.next is not None:  #
            slow = slow.next   # 慢结点一次走一步
            fast = fast.next.next  # 快结点一次走两步
            if slow and slow == fast:  # 如果快结点等于了慢结点,那么就说明有环
                circle = True
                break  # 直接出循环
        if not circle:
            return False  # 如果没有环直接退出
    
    	# 有环,找到环的入口点
        a = link.head   # a从第一个结点开始
        while a != fast:  # fast此时是相遇的结点
            a = a.next
            fast = fast.next   # a和fast都每次向后移动一步,直到相遇,相遇的地方就是环的入口
        return a.data  # 返回入口点的data
    

    测试
    先创建一个有环链表:
    链表环的入口点是3

    
    class Node:  # 创建一个结点类,用来生成结点
        def __init__(self, data):
            self.data = data
    
    class SingleLink:  # 创建一个单链表类
        def __init__(self):
            self.head = None
            self.tail = None
    
        def append(self, x):  # 尾部追加方法
            if self.head is None:
                self.head = self.tail = Node(x)
                return self
            self.tail.next = Node(x)
            self.tail = self.tail.next
            return self
    
    # 构造一个有环链表
    link1 = SingleLink()
    for i in range(1, 10):
        link1.append(i)
    link1.tail.next = link1.head.next.next  # 构造一个环
    

    分别调用两个函数,查看打印结果:

    # 快慢指针法
    def check(link: SingleLink):
        slow = fast = link.head  # 快慢指针都指向 第一个结点
        circle = False  # 假设没有环
        while slow and fast and fast.next is not None:  #
            slow = slow.next   # 慢结点一次走一步
            fast = fast.next.next  # 快结点一次走两步
            if slow and slow == fast:  # 如果快结点等于了慢结点,那么就说明有环
                circle = True
                break  # 直接出循环
        if not circle:
            return False
    
        a = link.head   # a从第一个结点开始
        while a != fast:  # fast此时是相遇的结点
            a = a.next
            fast = fast.next   # a和fast都每次向后移动一步,直到相遇,相遇的地方就是环的入口
        return a.data  # 返回入口点的data
        
    print(check(link1))  # 3
    
    ########################################
    # 哈希集合法:
    def check(link: SingleLink):
        s = set()  # 构造一个空的set
        p = link.head
        if p is None:  # 空链表直接返回False
            return False
        s.add(p)  # 不空的话把第一个结点加入到s中
        while p.next is not None:  # 如果当前结点的next不是空的话
            if p.next in s:  # 当前结点的下一个结点在集合中
                return p.next.data  # 入口就是当前结点的下一个结点
            s.add(p.next)  # 不在就把当前结点加入到集合中
            p = p.next
        return False  # 如果一直到结尾都没有,那就不是环,返回False
    
    print(check(link1))  # 3
    

    测试结果没问题。

  • 相关阅读:
    tensorflow 1
    BAT变量中的百分号学习
    mysqldump: unknown option '--no-beep'
    mysql数据库文件默认保存目录(windows)
    mysql 直接从date 文件夹备份表,还原数据库之后提示 table doesn`t exist的原因和解决方法
    淘宝开源Web服务器Tengine安装教程
    Solr开发文档
    spring线程池配置
    Host 'XXX' is not allowed to connect to this MySQL server 解决方案/如何开启MySQL的远程帐号
    Redis应用场景
  • 原文地址:https://www.cnblogs.com/duanming/p/11830261.html
Copyright © 2011-2022 走看看