zoukankan      html  css  js  c++  java
  • Python与数据结构[0] -> 链表/LinkedList[0] -> 单链表与带表头单链表的 Python 实现

    单链表 / Linked List


    目录

    1.  单链表
    2.  带表头单链表

    链表是一种基本的线性数据结构,在C语言中,这种数据结构通过指针实现,由于存储空间不要求连续性,因此插入和删除操作将变得十分快速。下面将利用Python来完成单链表的实现。

    1 单链表

    不带表头的单链表通常形式如下,

    node_1 -> node_2 -> node_3 -> node_4

    完整代码

      1 class Node:
      2     def __init__(self, val=None, nxt=None):
      3         self.value = val
      4         self.next = nxt
      5 
      6     def __str__(self):
      7         return str(self.value)
      8 
      9 
     10 class LinkedList:
     11     """
     12     Linked list: 
     13             node_1 -> node_2 -> node_3 -> node_4  
     14     """
     15     def __init__(self, iterable=()):
     16         self.header = None
     17         if iterable:
     18             self.init(iterable)
     19 
     20     def __str__(self):
     21         def _traversal(self):
     22             node = self.header
     23             while node and node.next:
     24                 yield node
     25                 node = node.next
     26             yield node
     27         return '->'.join(map(lambda x: str(x), _traversal(self)))
     28 
     29     def init(self, iterable=()):
     30         # Note: use empty tuple rather than list to init iterable
     31         if not iterable:
     32             return
     33         self.header = Node(iterable[0])     # header value
     34         node = self.header
     35         for i in iterable[1:]:              # add all node
     36             node.next = Node(i)
     37             node = node.next
     38 
     39     def show(self):
     40         print(self)
     41 
     42     @property
     43     def length(self):
     44         if self.header is None:
     45             return 0
     46         node = self.header      # node pointer points to header
     47         i = 1
     48         while node.next:
     49             node = node.next    # node pointer move to next
     50             i += 1
     51         return i
     52 
     53     @property
     54     def is_empty(self):
     55         return self.header is None
     56         
     57     def clear(self):
     58         self.__init__()
     59         # self.header = None
     60 
     61     def append(self, item):
     62         self.insert(item, self.length)
     63 
     64     def find(self, item):
     65         node = self.header
     66         while node.next and node.value != item:
     67             node = node.next
     68         if node.value == item:
     69             return node
     70         return None
     71 
     72     def find_previous(self, item):
     73         node = self.header
     74         while node.next and node.next.value != item:
     75             node = node.next
     76         if node.next and node.next.value == item:
     77             return node
     78         return None
     79 
     80     def delete(self, item):
     81         '''
     82         node_1 -- X --> node_2 -----> node_3
     83                                 /
     84                                /
     85               ------------------
     86         '''
     87         prev = self.find_previous(item)
     88         if prev:
     89             prev.next = prev.next.next
     90 
     91     def insert(self, item, index):
     92         '''
     93                 ----> node_2 ---
     94                /                
     95               /                  
     96         node_1 -------  X  ---------> node_3
     97 
     98         '''
     99         if abs(index) > self.length:
    100             return
    101         if index < 0:
    102             self.insert(item, self.length+index+1)
    103             return
    104         elif index == 0:
    105             self.insert(self.header.value, 1)
    106             self.header.value = item
    107             return
    108         node = self.header
    109         i = 0
    110         while i < index-1:
    111             node = node.next
    112             i += 1
    113         n = node.next 
    114         node.next = Node(item, n)
    115 
    116 
    117 def test(li):
    118     print('Show linked list:')
    119     li.show()
    120 
    121     print('
    Init linked list:')
    122     li.init([1, 2, 3, 4, 5, 6, 'xd', 8, 9])
    123     li.show()
    124 
    125     print('
    Insert element:')
    126     li.insert('xxd', -3)
    127     li.show()
    128 
    129     print('
    Append element:')
    130     li.append('10')
    131     li.show()
    132 
    133     e = 'xd'
    134     print('
    Find element:')
    135     x = li.find(e)
    136     print(x.value if x else x)
    137     print('
    Find previous element:')
    138     x = li.find_previous(e)
    139     print(x.value if x else x)
    140 
    141     print('
    Delete element:')
    142     li.delete('xd')
    143     li.show()
    144 
    145     print('
    Find element not exist:')
    146     x = li.find(e)
    147     print(x.value if x else x)
    148 
    149     print('
    Insert element to header:')
    150     li.insert('cc', 0)
    151     li.show()
    152  
    153     print('
    Clear linked list:')
    154     li.clear()
    155     li.show()
    156 
    157     print('
    Current length: %s' % li.length)
    158     print('
    Is empty: %s' % li.is_empty)
    159    
    160 
    161 if __name__ == '__main__':
    162     test(LinkedList())
    View Code

    分段解释

    首先,需要定义一个结点类(在C语言中则使用结构体进行定义),其中包括两个基本属性,当前值和下个结点。由于Python中无法使用类似C语言中的指针,因此只能通过变量的引用来实现类似指针操作的功能。为了更好的显示结点,这里重载了结点的内置函数__str__。

    1 class Node:
    2     def __init__(self, val=None, nxt=None):
    3         self.value = val
    4         self.next = nxt
    5 
    6     def __str__(self):
    7         return str(self.value)

    接着,我们需要。定义一个链表类,并利用前面的结点来构建这个链表。链表类中包含了一个头结点属性,在初始化函数中,首先将头结点赋值None,类似于在C语言中将头结点指针指向NULL。初始化方法中定义了一个默认参数,使得链表可以在实例化的时候利用传入的可迭代对象进行链表生成。

    1 class LinkedList:
    2     """
    3     Linked list:
    4             node_1 -> node_2 -> node_3 -> node_4
    5     """
    6     def __init__(self, iterable=()):
    7         self.header = None
    8         if iterable:
    9             self.init(iterable)

    重载链表的__str__方法,在显示链表的时候会对链表进行遍历,然后以更加清晰的方式显示在输出终端上。

    1     def __str__(self):
    2         def _traversal(self):
    3             node = self.header
    4             while node and node.next:
    5                 yield node
    6                 node = node.next
    7             yield node
    8         return '->'.join(map(lambda x: str(x), _traversal(self)))

    定义链表的生成函数,函数中接收可迭代对象并生成新的链表。注意这里最好使用不可变的元组而不是可变的列表。

    在生成时,首先将第一个元素作为头结点,然后依次将每个结点的next属性引用指向下一个结点。

    1     def init(self, iterable=()):
    2         # Note: use empty tuple rather than list to init iterable
    3         if not iterable:
    4             return
    5         self.header = Node(iterable[0])     # header value
    6         node = self.header
    7         for i in iterable[1:]:              # add all node
    8             node.next = Node(i)
    9             node = node.next

    链表的show方法,由于已经重载了__str__方法,这里只需要print自身即可。

    1     def show(self):
    2         print(self)

    链表的length属性方法,用于返回链表的长度,利用装饰器定义成属性方法,调用时遍历链表计算长度最后返回即可。

     1     @property
     2     def length(self):
     3         if self.header is None:
     4             return 0
     5         node = self.header      # node pointer points to header
     6         i = 1
     7         while node.next:
     8             node = node.next    # node pointer move to next
     9             i += 1
    10         return i

    链表的is_empty属性方法,用于检测链表是否为空,只需要查看头结点是否为None即可。

    1     @property
    2     def is_empty(self):
    3         return self.header is None

    链表的clear方法,用于清空链表,可以选择直接初始化链表或将链表的头结点指向None。

    1     def clear(self):
    2         self.__init__()
    3         # self.header = None

    链表的append方法,用于在链表尾部添加一个元素结点,这里只要直接调用后面的insert方法并插入到链表长度的那个位置即可。

    1     def append(self, item):
    2         self.insert(item, self.length)

    链表的find方法,用于在链表中查找并返回某个结点,查找时会从链表的头部开始,遍历查找,直到找到查找的值或遇到None,这里的find方法有两点需要注意:

    1. 查找是依据元素的值进行查找,返回找到的第一个结点;
    2. 这里的查找方法如果遇到有环链表将无法自行检测退出

    最后返回查找到的结点或None。

    1     def find(self, item):
    2         node = self.header
    3         while node.next and node.value != item:
    4             node = node.next
    5         if node.value == item:
    6             return node
    7         return None

    find_previous与find类似,返回查找结点的前一个结点。

    1     def find_previous(self, item):
    2         node = self.header
    3         while node.next and node.next.value != item:
    4             node = node.next
    5         if node.next and node.next.value == item:
    6             return node
    7         return None

    delete方法用于删除结点,基本思路在于,找到需要删除结点node_2的前一个结点node_1,将前一个结点node_1的next指针指向删除结点的下一个结点node_3,从而惰性删除了这个结点。

    查找前一个结点可以使用前面定义的find_previous方法。

     1     def delete(self, item):
     2         '''
     3         node_1 -- X --> node_2 -----> node_3
     4                                 /
     5                                /
     6               ------------------
     7         '''
     8         prev = self.find_previous(item)
     9         if prev:
    10             prev.next = prev.next.next

    insert方法用于向链表中插入一个结点,

    基本思路在于,找到需要插入的位置前后的两个结点node_1和node_3,将node_1.next指向需要插入的结点node_2,再将node_2.next指向node_3,便完成了插入操作。

    若插入的位置为头结点之前,则会替换原来的头结点为新插入的结点。

    同时,这里的插入函数还支持index为负数的情况,类似于列表的负数索引。

     1     def insert(self, item, index):
     2         '''
     3                 ----> node_2 ---
     4                /                
     5               /                  
     6         node_1 -------  X  ---------> node_3
     7 
     8         '''
     9         if abs(index) > self.length:
    10             return
    11         if index < 0:
    12             self.insert(item, self.length+index+1)
    13             return
    14         elif index == 0:
    15             self.insert(self.header.value, 1)
    16             self.header.value = item
    17             return
    18         node = self.header
    19         i = 0
    20         while i < index-1:
    21             node = node.next
    22             i += 1
    23         n = node.next 
    24         node.next = Node(item, n)

    最后,定义一个测试函数用于测试链表的各种操作。

    开始时显示空表

    1 def test(li):
    2     print('Show linked list:')
    3     li.show()

    得到结果

    Show linked list:
    None

    接着初始化链表元素

    1     print('
    Init linked list:')
    2     li.init([1, 2, 3, 4, 5, 6, 'xd', 8, 9])
    3     li.show()

    得到结果

    Init linked list:
    1->2->3->4->5->6->xd->8->9

    再向链表中插入元素

    1     print('
    Insert element:')
    2     li.insert('xxd', -3)
    3     li.show()

    得到结果

    Insert element:
    1->2->3->4->5->6->xd->xxd->8->9

    向链表末尾添加元素

    1     print('
    Append element:')
    2     li.append('10')
    3     li.show()

    得到结果

    Append element:
    1->2->3->4->5->6->xd->xxd->8->9->10

    查找元素

    1     e = 'xd'
    2     print('
    Find element:')
    3     x = li.find(e)
    4     print(x.value if x else x)
    5     print('
    Find previous element:')
    6     x = li.find_previous(e)
    7     print(x.value if x else x)

    得到结果

    Find element:
    xd
    
    Find previous element:
    6

    删除元素

    1     print('
    Delete element:')
    2     li.delete('xd')
    3     li.show()

    得到结果

    Delete element:
    1->2->3->4->5->6->xxd->8->9->10

    查找不存在的元素

    1     print('
    Find element not exist:')
    2     x = li.find(e)
    3     print(x.value if x else x)

    得到结果

    Find element not exist:
    None

    替换头结点

    1     print('
    Insert element to header:')
    2     li.insert('cc', 0)
    3     li.show()

    得到结果

    Insert element to header:
    cc->1->2->3->4->5->6->xxd->8->9->10

    清空链表并查看链表信息

    1     print('
    Clear linked list:')
    2     li.clear()
    3     li.show()
    4 
    5     print('
    Current length: %s' % li.length)
    6     print('
    Is empty: %s' % li.is_empty)

    得到结果

    Clear linked list:
    None
    
    Current length: 0
    
    Is empty: True

    2 带表头单链表

    这种单链表带有一个标志结点,通常被称为表头或哑结点,表头通常位于位置0处,通常不会被改变。

    Linked list with dummy header node:
           Header -> node_1 -> node_2 -> node_3

    带表头的单链表在实现时与不带表头的单链表类似,因此可以继承前面的单链表类来派生出一个带表头单链表类,其中需要进行修改和重载的方法主要有生成链表、判断链表为空和插入链表这几个方法。

     1 from linked_list import Node, LinkedList, test
     2 
     3 
     4 class LinkedListDummyHeader(LinkedList):
     5     """
     6     Linked list with dummy header node:
     7             Header -> node_1 -> node_2 -> node_3
     8     """
     9     def __init__(self, iterable=()):
    10         self.header = Node('Header', None)
    11         if iterable:
    12             self.init(iterable)
    13 
    14     def init(self, iterable=()):
    15         if not iterable:
    16             return
    17         node = self.header
    18         for i in iterable:
    19             node.next = Node(i)
    20             node = node.next
    21 
    22     @property
    23     def is_empty(self):
    24         return self.header.next is None
    25         # if self.length == 1:
    26         #     return True
    27         # return False
    28 
    29     def insert(self, item, index):
    30         if index == 0:
    31             return
    32         super(LinkedListDummyHeader, self).insert(item, index)
    33 
    34 if __name__ == '__main__':
    35     test(LinkedListDummyHeader())

    __init__方法:

    初始化时头结点不再指向None,而是指向表头。

    init方法:

    由于有表头的存在,因此创建初始链表的方法首先需要建立一个表头,再将所有元素依次加入链表中。

    is_empty方法:

    由于表头的存在,此时判断是否为空则可以根据表头指向结点是否为空,或链表长度是否为1来进行。

    insert方法:

    此处需要修改的地方在于当插入点为表头时,则应该直接返回或引发异常,无法修改表头。

    最后利用单链表的测试函数进行测试得到结果

    Show linked list:
    Header
    
    Init linked list:
    Header->1->2->3->4->5->6->xd->8->9
    
    Insert element:
    Header->1->2->3->4->5->6->xd->xxd->8->9
    
    Append element:
    Header->1->2->3->4->5->6->xd->xxd->8->9->10
    
    Find element:
    xd
    
    Find previous element:
    6
    
    Delete element:
    Header->1->2->3->4->5->6->xxd->8->9->10
    
    Find element not exist:
    None
    
    Insert element to header:
    Header->1->2->3->4->5->6->xxd->8->9->10
    
    Clear linked list:
    Header
    
    Current length: 1
    
    Is empty: True
  • 相关阅读:
    elementui 修改合计行样式
    C# 导出Excel NPOI 修改指定单元格的样式 或者行样式
    向基于语义模型的操作集成的演变
    在制造业的工业2.0中应用MOM系统
    制造运营管理 (MOM) 的工作流驱动方法
    语义模型在智能工业运营中的作用
    定义运营系统架构
    在离散混合制造环境中应用制造运营模型
    面试官:如果存取IP地址,用什么数据类型比较好 (C#版本)
    使用WtmPlus低代码平台提高生产力
  • 原文地址:https://www.cnblogs.com/stacklike/p/8283988.html
Copyright © 2011-2022 走看看