Python 链表与环路检测算法

  1. Python 链表

  python 链表可以实现如下:

# 链表节点类
class Node:
    def __init__(self, value, Next):
        self.value = value
        self.next = Next


# 链表类
class LinkedList(Node):
    # 初始化链表
    def __init__(self):
        # 覆盖父类构造函数
        super(LinkedList, self).__init__(0, None)
        self.size = 0
        # 尾指针、头指针(哨兵)
        self.tail = self.sentinel = Node(value=self.value, Next=None)

    # 尾部追加
    def append_(self, value):
        self.tail.next = Node(value=value, Next=None)
        self.tail = self.tail.next
        self.size += 1

    # 打印函数
    def __str__(self):
        cell = self.sentinel.next
        while cell:
            print(cell.value)
            cell = cell.next

2.环路检测算法

  • 标记法

    
def hasLoopMark(self):
        # 采用标记单元格的方式来 判断是否有环
        """
        在Node中多加一个数据域 visited = [False]*self.size

        算法执行过程:
            从sentinel开始访问链表每一个节点,判断节点visited是否为True,
            若是,则说明链表存在环,node.next = None 破环
            时间复杂度 = O(N)
            空间复杂度 = N
        :return: boolean

        """
        
        # 能检测环, 并能破环,算法直接易于理解,实现过程简单
        pass
  • 使用哈希表(散列表)

    def hasLoopHashTable(self) -> bool:
        # 使用哈希表(散列表)
        """
        定义一个哈希表,哈希表的大小应为 1.5*self.size,即链表长度的1.5倍,哈希表具有比较好的性能
        算法执行过程:
            从sentinel开始访问每一个节点,检查是否哈希表是否已存在该节点,若存在则存在环,
            返回True,否则,将该节点加入哈希表
        时间复杂度 = O(N)
        空间复杂度 = N
        :return:boolean
        """
        
        # 需要一个hashTable,能检测并破环,容易实现
        pass
  • 回溯法

  •     def hasLoopLinkRetracing(self):
            # 链表回溯法
            """
            使用两个Node对象,遍历链表,当两个对象不相等,且next一致时,说明检测到了环
            时间复杂度 = O(N**2)
            空间复杂度 = 1
            :param self:
            :return:
            """
            node_ = self.sentinel
            while node_.next:
                # node不断往前推进,对每个node,tracer都从sentinel开始检测,直到与node相遇或检测到环(if条件)结束算法
                tracer = self.sentinel
                while tracer != node_:
                    if tracer.next == node_.next:
                        node_.next = None
                        return True
                    tracer = tracer.next
                node_ = node_.next
            # 能检测并破环
  • 龟兔赛跑算法(弗洛伊德循环查找算法)

算法原理:

龟兔跑道如图(有环路的链表)

直跑道:T个单元格

环跑道:L个单元格

检测是否有环:若兔子从sentinel开始跑,若找到空节点,则无环

破环:

第一阶段(寻找循环起始位置start):兔子一步两个单元格,乌龟一步一个单元格。

  • 从sentinel开始,乌龟跑T个单元格后,位于start,此时兔子已走过2T个单元格,位于距离start H = T mod L处,则龟兔相距L-H,

       由于兔子一步比乌龟多走一格,故当乌龟走L-H步时,而这相遇,相遇在距离start L-H处;

  • 兔子再相遇过后,立即从sentinel以一步一格的速度出发,T步后,兔子来到start处,而乌龟位置为L-H+TmodL,即等于L,也即龟兔再次相遇在start,即找到start。

第二阶段(找到循环结束位置End):

兔子(或乌龟)继续往前走,直到兔子(或乌龟)的下一个单元格为乌龟(或兔子)所在单元格,则这个单元格为end

令end.next = None即可破环

算法执行过程:

1)让兔子一步两个单元格,乌龟一步一个单元格地移动

2)若兔子从sentinel开始跑,若找到空节点,则无环

3)否则,当兔子赶上乌龟时,兔子从链表开始出一步一个单元格的重新跑,乌龟继续移动

4)当兔子乌龟再次相遇时,相遇节点为start节点

5)兔子(或乌龟)继续往前走,直到兔子(或乌龟)的下一个节点为乌龟(或兔子)所在节点,则这个节点为end

  • 反转链表法

算法原理:若有有环,则反转后,sentinel(头指针、哨兵)位置不变;若无环,反转后sentinel(头指针、哨兵)位置会挪到最初链表最后

原理图解:

  1. 最初有环链表

   2.反转  一

明白人会发现,此时并不算反转完成

3.反转 二

 

可见,有环路的链表,反转过后sentinel位置不变,因此可以通过反转链表,凭借sentinel位置是否改变来检测是否有环,

遗憾的是,这个算法不能破环。

反转链表Python实现:

 # 反转链表
    def reverseList(self):
        pre_node = None
        curr_node = self.sentinel.next

        while curr_node:

            next_node = curr_node.next

            curr_node.next = pre_node

            pre_node = curr_node

            curr_node = next_node
        # 将sentinel移到反转后的链表头部
        self.sentinel.next = pre_node

 

PS:代码clone地址:git@github.com:Rlyslata/DataStauct-And-Algorithm.git

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值