1.6带环单链表

Posted miao-study

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1.6带环单链表相关的知识,希望对你有一定的参考价值。

检测单链表中是否有环

方法一:蛮力法

定义一个集合用来存放结点的引用,并将其初始化为空,从链表的头结点开始向后遍历,每遍历到一个结点就判断集合中是否有这个结点的引用,如果没有,说明这个结点是第一次访问,还没有形成环,那么将这个结点的引用添加到集合中去。如果在集合中找到了同样的结点,那么说明这个结点已经被访问过了,于是就形成了环。这种方法的时间复杂度为O(n),空间复杂度也为O(n)。

方法二 : 快慢指针遍历法

定义两个指针fast(快)与slow(慢),二者的初始值都指向链表头,指针slow每次前进一步,指针fast每次前进两步,两个指针同时向前移动,快指针每移动一次都要跟慢指针比较,如果快指针等于慢指针,就证明这个链表是带环的链表。

找出环的入口处并解环

如果单链表有环,那么按照上述方法二的思路,当走得快的指针fast与走得慢的指针slow相遇时,slow 指针肯定没有遍历完链表,而fast指针己经在环内循环了n圈 (n>=1)。如果slow 指针走了s步,则fast指针走了2s步,fast步数还等于s加上在环上多转的n圈,假设环长为r,则满足如下关系表达式:
2s = s +or
由此可以得到 : s=nr
设整个链表长为L,入口环与相遇点距离为x,起点到环入口点的距离为a。则满足如下关系表达式:
a+x=nr
a+x=(n-1)r+r=(n-l)r+L-a
a=(n-l)r+(L-ax)
(L-a-x)为相遇点到环入口点的距离,从链表头到环入口点的距离=(n-l)*环长+相遇点到环入口 点的长度,于是从链表头与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。将相遇点指针的前一个节点的next域设成None即可解环。

代码实现:

# -*-coding:utf-8-*- 
"""
@Author  : 图南
@Software: PyCharm
@Time    : 2019/9/6 15:28
"""


class Node:
    def __init__(self, data=None, next=None):
        self.data = data
        self.next = next


# 构造单链表
def con_link(s, k):
    head = Node()
    cur = head
    nums = list(map(int, s.split(' ')))
    k = int(k)
    for num in nums:
        node = Node(num)
        cur.next = node
        cur = node
    tmp = head
    if k != 0:
        for i in range(k):
            tmp = tmp.next
        cur.next = tmp
    return head


# 打印单链表(用到方法一中的思想)
def print_link(head):
    if head is None or head.next is None:
        return None
    flag = []
    cur = head.next
    while cur:
        if cur not in flag:
            flag.append(cur)
            print(cur.data, end=' ')
            cur = cur.next
        else:
            print(cur.data, end=' ')
            break
    print()


# 判断链表中是否有环
def isLoop(head):
    if head is None or head.next is None:
        return None
    fast = head.next
    slow = head.next
    while True:
        try:
            fast = fast.next.next
            slow = slow.next
        except Exception:
            print("该链表中没有环!")
            print_link(head)
            return False, head
        if fast == slow:
            print("该链表中有环!")
            print_link(head)
            return True, fast


# 查找环的入点口,并解环
def findLoopNode(head, fast):
    cur = head.next
    pre = None
    while cur != fast:
        pre = fast
        cur = cur.next
        fast = fast.next
    print(cur.data)
    # 解环并打印链表
    pre.next = None
    print_link(head)


if __name__ == '__main__':
    nums = input('链表:')
    # 输入环的入口点,若为0则链表中无环
    k = input('环的入点口:')
    head = con_link(nums, k)
    f, fast = isLoop(head)
    if f:
        findLoopNode(head, fast)

运行结果:

带环单链表

技术图片
无环单链表

技术图片

以上是关于1.6带环单链表的主要内容,如果未能解决你的问题,请参考以下文章

单链表含环的各种面试题

判断单链表是否带环,如果带环,求环的长度和入口结点

判断单链表是否带环,如果带环,求环的长度和入口结点

单链表 --- 环相关问题(是否存在环是否相交)

判断链表是否带环,以及环的入口

链表:判断链表是否带环 求两个链表的相交结点