• python实现 双向循环链表


    最近身边的朋友在研究用python来实现数据结构。遇到一个问题就是双向循环链表的实现,改指向的时候总是发蒙。

    我自己尝实现了一个python的双向循环链表。附上代码,希望对大家有帮助。

    如果不懂什么是双向循环链表的伙伴,需要补习一下数据结构的基础之后哦~~~

    在python当中 用一个类Node 来实现链表的节点,节点数据有三个变量:

      prev:前驱指针: 用于指向当前节点前一个节点

      next: 后继指针  用于指向当前节点后一个节点

      item: 值, 用于存储该节点要存的数值

      当前节点的前一个节点我们叫他前驱,后一个节点我们叫他后继。

    在链表类当中,我们有一个变量head是链表的头指针

    我们拿着链表的头head,就可以对他进行一些列操作:( 由于是双向循环链表,修改指针特别容易出错,我尽量说的细致,方便大家参考)

    判断空:  is_empty()

      如果头指针head没有指向则链表是空的 否则不是空的

    在头部添加元素: add(item)

      1 新建一个节点 里面的值是item。

      2 放入头部:

        2.1 如果链表是空的,node的next和prev都指向自己,然后head再指向node

    在尾部添加元素: append(item)

      1 创建一个新节点node 里面的值是item

      2 放入尾部:

        2.1 如果链表空,则执行头部添加add就可以

        2.2 链表非空:

          2.2.1 node的next指向head

          2.2.2 node的prev指向head的prev

          2.2.3 head的prev元素的next指向node

          2.2.4 head的prev指向改为node

          2.2.5 head指向node  更换了头部

    指定位置添加元素: insert( pos , item )

       1 新建一个节点node 里面的值是item

       2 找到合适的位置插进去:

          2.1 如果pos <= 0 还小,那就执行头插方法 add()

          2.2 如果pos >= 链表长度, 那就执行尾部插入 append()

          2.3 如果pos位置在链表的中间:

            2.3.1 定义一个临时变量temp 按照传入的pos找到要插入的位置的前一个元素

            2.3.2 node的prev设为temp,node的next设为temp的next

            2.3.3 temp的next指向的节点的prev改为node

            2.3.4 temp的next改为node

    得到链表长度: length()

       1 我们设置一个临时变量temp初始设为head , 设置一个计数器count 初始为 0

       2 令count自增1 然后temp改指向自己的下一个元素 一直到temp遇到None 为止,temp到了链表的最后一个元素

        通过这样的方式,统计出一共有多少个节点返回

    遍历链表数据: travelji()

       1 设置一个临时变量temp初始化设为head

       2 temp 每次输出自己指向元素的值,然后在指向自己的下一个元素,一直temp为None 说明到了列表的尾部

    删除链表元素: remove( item )

      1 开启temp临时变量 初始化为head , 

      2  temp不断指向自己的下一个元素,每次指向一个元素都检查当前值是不是item,如果找到item则删除它返回True,如果没找到就到尾部了就返回False

        2.1 删除过程:

          2.1.1 temp的前一个元素的next改为temp的后一个元素

          2.1.2 temp的后一个元素的prev改为前一个元素

    查询是否有元素:search()

      设置一个临时变量temp从head开始,不断指向自己下一个,每次都检查一下自己的值如果和item相同返回True结束

      如果temp变成None 则到尾部了都没找到 返回False

    上代码!

      1 #链表的节点
      2 class Node(object):
      3     def __init__(self , item ):
      4         self.item = item    #节点数值
      5         self.prev = None    #用于指向前一个元素
      6         self.next = None    #用于指向后一个元素
      7 #双向循环链表
      8 class DoubleCircleLinkList(object):
      9     def __init__(self):
     10         self.__head = None  #初始化的时候头节点设为空、
     11     #判断链表是否为空,head为None 的话则链表是空的
     12     def is_empty(self):
     13         return self.__head is None
     14     #头部添加元素的方法
     15     def add(self,item):
     16         node = Node(item)   #新建一个节点node 里面的值是item
     17         # 如果链表是空的,则node的next和prev都指向自己(因为是双向循环),head指向node
     18         if self.is_empty():
     19             self.__head = node
     20             node.next = node
     21             node.prev = node
     22         # 否则链表不空
     23         else:
     24             node.next = self.__head #node的next设为现在的head
     25             node.prev = self.__head.prev #node的prev 设为现在head的prev
     26             self.__head.prev.next = node  #现在head的前一个元素的next设为node
     27             self.__head.prev = node #现在head的前驱 改为node
     28             self.__head = node #更改头部指针
     29     #尾部添加元素方法
     30     def append(self , item):
     31         #如果当前链表是空的 那就调用头部插入方法
     32         if self.is_empty():
     33             self.add(item)
     34         #否则链表不为空
     35         else :
     36             node = Node(item)   #新建一个节点node
     37             #因为是双向循环链表,所以head的prev其实就是链表的尾部
     38             node.next = self.__head #node的下一个设为头
     39             node.prev = self.__head.prev    #node的前驱设为现在头部的前驱
     40             self.__head.prev.next = node    #头部前驱的后继设为node
     41             self.__head.prev = node #头部自己的前驱改为node
     42     #获得链表长度 节点个数
     43     def length(self):
     44         #如果链表是空的 就返回0
     45         if self.is_empty():
     46             return 0
     47         #如果不是空的
     48         else:
     49             cur = self.__head   #临时变量cur表示当前位置 初始化设为头head
     50             count = 1   #设一个计数器count,cur每指向一个节点,count就自增1  目前cur指向头,所以count初始化为1
     51             #如果cur.next不是head,说明cur目前不是最后一个元素,那么count就1,再让cur后移一位
     52             while cur.next is not self.__head:
     53                 count += 1
     54                 cur = cur.next
     55             #跳出循环说明所有元素都被累加了一次 返回count就是一共有多少个元素
     56             return count
     57     #遍历链表的功能
     58     def travel(self):
     59         #如果当前自己是空的,那就不遍历
     60         if self.is_empty():
     61             return
     62         #链表不空
     63         else :
     64             cur = self.__head   #临时变量cur表示当前位置,初始化为链表的头部
     65             #只要cur的后继不是头说明cur不是最后一个节点,我们就输出当前值,并让cur后移一个节点
     66             while cur.next is not self.__head:
     67                 print( cur.item,end=" " )
     68                 cur = cur.next
     69             #当cur的后继是head的时候跳出循环了,最后一个节点还没有打印值 在这里打印出来
     70             print( cur.item )
     71 
     72     #置顶位置插入节点
     73     def insert(self, pos , item ):
     74         #如果位置<=0 则调用头部插入方法
     75         if pos <= 0:
     76             self.add(item)
     77         #如果位置是最后一个或者更大 就调用尾部插入方法
     78         elif pos > self.length() - 1 :
     79             self.append(item)
     80         #否则插入位置就是链表中间
     81         else :
     82             index = 0   #设置计数器,用于标记我们后移了多少步
     83             cur = self.__head   #cur标记当前所在位置
     84             #让index每次自增1 ,cur后移,当index=pos-1的时候说明cur在要插入位置的前一个元素,这时候停下
     85             while index < pos - 1 :
     86                 index += 1
     87                 cur = cur.next
     88             #跳出循环,cur在要插入位置的前一个元素,将node插入到cur的后面
     89             node = Node(item) #新建一个节点
     90             node.next = cur.next    #node的后继设为cur的后继
     91             node.prev = cur #node的前驱设为cur
     92             cur.next.prev = node    #cur后继的前驱改为node
     93             cur.next = node #cur后继改为node
     94     #删除节点操作
     95     def remove(self,item):
     96         #如果链表为空 直接不操作
     97         if self.is_empty():
     98             return
     99         #链表不为空
    100         else:
    101             cur = self.__head   #临时变量标记位置,从头开始
    102             #如果头结点就是 要删除的元素
    103             if cur.item == item:
    104                 #如果只有一个节点 链表就空了 head设为None
    105                 if self.length() == 1:
    106                     self.__head = None
    107                 #如果多个元素
    108                 else:
    109                     self.__head = cur.next #头指针指向cur的下一个
    110                     cur.next.prev= cur.prev #cur后继的前驱改为cur的前驱
    111                     cur.prev.next = cur.next #cur前驱的后继改为cur的后继
    112             #否则 头节点不是要删除的节点 我们要向下遍历
    113             else:
    114                 cur = cur.next  #把cur后移一个节点
    115                 #循环让cur后移一直到链表尾元素位置,期间如果找得到就删除节点,找不到就跳出循环,
    116                 while cur is not self.__head:
    117                     #找到了元素cur就是要删除的
    118                     if cur.item == item:
    119                         cur.prev.next = cur.next    #cur的前驱的后继改为cur的后继
    120                         cur.next.prev = cur.prev    #cur的后继的前驱改为cur的前驱
    121                     cur = cur.next
    122     #搜索节点是否存在
    123     def search(self , item):
    124         #如果链表是空的一定不存在
    125         if self.is_empty():
    126             return False
    127         #否则链表不空
    128         else:
    129             cur = self.__head   #设置临时cur从头开始
    130             # cur不断后移,一直到尾节点为止
    131             while cur.next is not self.__head:
    132                 #如果期间找到了就返回一个True 结束运行
    133                 if cur.item == item:
    134                     return True
    135                 cur = cur.next
    136             # 从循环跳出来cur就指向了尾元素 看一下为元素是不是要找的 是就返回True
    137             if cur.item ==item:
    138                 return True
    139             #所有元素都不是 就返回False 没找到
    140             return False
    141 
    142 
    143 if __name__ == "__main__":
    144     dlcl = DoubleCircleLinkList()
    145     print(dlcl.search(7))
    146     dlcl.travel()
    147     dlcl.remove(1)
    148     print(dlcl.length())
    149     print(dlcl.is_empty())
    150     dlcl.append(55)
    151     print(dlcl.search(55))
    152     dlcl.travel()
    153     dlcl.remove(55)
    154     dlcl.travel()
    155     print(dlcl.length())
    156     dlcl.add(3)
    157     print(dlcl.is_empty())
    158     dlcl.travel()
    159     dlcl.add(4)
    160     dlcl.add(5)
    161     dlcl.append(6)
    162     dlcl.insert(-10,1)
    163     dlcl.travel()
    164     print(dlcl.length())
    165     dlcl.remove(6)
    166     dlcl.travel()
    167 
    168     print(dlcl.search(7) )
    169     dlcl.append(55)
    170     dlcl.travel()

    各种数据结构主要是思想,不同的人实现方式都不一定一样,同一个人多次实现也不一定一样。所以以上代码仅供参考~

    如果有更简洁的方式,欢迎大家批评指正哦~

  • 相关阅读:
    使用pdf.js快速实现pdf查看器
    简单了解一下pinia的结构
    【Spring】 @ConditionalOnExpression 满足条件才初始化Bean 的奇技淫巧
    【mybatis】mybatis 使用association关联查询的几种方式
    【mysql】关于java、Spring、mybatis、mysql项目中数据库datetime存入取出时间差一秒的问题解决方案
    【java】【ExecutorService】【线程池】java 超时返回,异步线程实现超时响应
    消息重发、重试消费、死信队列
    RocketMQ简单使用(二)批处理、过滤、事务消息
    基于SpringMVC自定义HandlerMapping扩展Handler&注册自己的Servlet
    django REST框架 Djangoninja
  • 原文地址:https://www.cnblogs.com/Lin-Yi/p/7326713.html
Copyright © 2020-2023  润新知