• Python数据结构-链表


    数据结构-链表

    链表是实现了数据之间保持逻辑顺序,但存储空间不必按顺序的方法。可以用一个图来表示这种链表的数据结构:

    图1:链表

     

    链表中的基本要素:

    1. 结点(也可以叫节点或元素),每一个结点有两个域,左边部分叫:值域,用于存放用户数据;右边叫:指针域,一般是存储着到下一个元素的指针
    2. head结点,head是一个特殊的结节,head结点永远指向第一个结点
    3. tail结点,tail结点也是一个特殊的结点,tail结点永远指向最后一个节点
    4. None,链表中最后一个结点指针域的指针指向None值,因也叫:接地点,所以有些资料上用电气上的接地符号代表None

    链表的常用方法:

    1. LinkedList() 创建空链表,不需要参数,返回值是空链表
    2. is_empty() 测试链表是否为空,不需要参数,返回值是布尔值
    3. append(data) 在尾部增加一个元素作为列表最后一个。参数是要追加的元素,无返回值
    4. iter() 遍历链表,无参数,无返回值,此方法一般是一个生成器
    5. insert(idx,value) 插入一个元素,参数为插入元素的索引和值
    6. remove(idx)移除1个元素,参数为要移除的元素或索引,并修改链表
    7. size() 返回链表的元素数,不需要参数,返回值是个整数
    8. search(item) 查找链表某元素,参数为要查找的元素或索引,返回是布尔值

    节点类

    python用类来实现链表的数据结构,节点(Node)是实现链表的基本模块,每个节点至少包括两个重要部分。首先,包括节点自身的数据,称为“数据域”(也叫值域)。其次,每个节点包括下一个节点的“引用”(也叫指针)

    下边的代码用于实现一个Node类:

    1 class Node:
    2     '''定义一个节点类'''
    3     def __init__(self, data):
    4         self.data = data
    5         self.next = None

    此节点类只有一个构建函数,接收一个数据参数,其中next表示指针域的指针,实例化后得到一个节点对象,如下:

    1 node = Node(4)

    此节点对象数据为4,指针指向None。

    这样一个节点对象可以用一个图例来更形象的说明,如下:

    图2:节点

    链表类

    先来看LinkedList类的构建函数:

    1 class LinkedList:
    2     '''构建LinkedList类'''
    3     def __init__(self):
    4         self.head = None
    5         self.tail = None

    此类实例后会生成一个链表对象,初始化了headtail节点,且两节点都指向None,实例化代码如下:

    1 link_list = LinkedList()

    也可以用图形象的表示这个链表对象,如下:

    图3:空链表

    is_empty方法实现

    is_empty方法检查链表是否是一个空链表,这个方法只需要检查head节点是否指向None即可,代码如下:

    1     def is_empty(self):
    2         return self.head is None

    如果是空列表返回True,否则返回False

    append方法实现

    append方法表示增加元素到链表,这和insert方法不同,前者使新增加的元素成为链表中第一个节点,而后者是根据索引值来判断插入到链表的哪个位置。代码如下:

    1     def append(self, data):
    2         node = Node(data)
    3         if self.head is None:
    4             self.head = Node
    5             self.tail = Node
    6         else:
    7             self.tail.next = node
    8             self.tail = node

    既然要新增加节点,首先把Node类实例化得到一个node对象。这里有两种情况需要考虑,一是链表是一个空链表时怎样append一个节点;二是当链表不是空链表时又怎样append一个节点?

    if self.head is None: -> True时,把链表的headtail都指向了node,假如我们执行了

    1 link_list(append(4))

    此时的链表结构如下图:

    图4:append-1

     当if self.head is None: -> False时,说明链表已经增加了一个节点了,再增加一个节点时head已经指向了第一个节点,所以不为None,比如增加的第二个节点为:

    1 link_list(append(5))

    增加第二个节点的操作需要分两步完成,第一步:self.tail.next = node,即把上一个节点的next指针指向当前node;第二步:self.tail = node,把tail移动到node,如下图:

    图5:append-2

     移动完成后就成这样了:

    图6:append-3

    当增加第三个、第四个等节点时,按照上边的操作依次类推。

    iter方法实现

    iter方法表示遍历链表。在遍历链表时也要首先考虑空链表的情况。遍历链表时从head开始,直到一个节点的next指向None结束,代码如下:

    1     def iter(self):
    2         if not self.head:
    3             return
    4         cur = self.head
    5         yield cur.data
    6         while cur.next:
    7             cur = cur.next
    8             yield cur.data

    当是遍历一个空链表时,if not self.head : -> True,直接返回None;如果不是空链表就让一个局部变量cur指向head,并把head的data属性yield出来,再对cur的next指针指向的对象做while循环,直到next指向None,这样就遍历了链表。

    insert方法实现

    假如采取append方法又增加了两个节点,增加完成后如下图:

    图7:insert-1

    如果想在数据域为6的那节点处插入一个节点,需要做的操作有两步:

    1. 把新节点的next指针指向数据域为6的这个节点,即为数据域为5节点的next指向指向的对象
    2. 把数据域为5节点的next指针指向新加的节点

    注: 这两个步骤不能颠倒,如果颠倒,数据域为6的节点会被丢失,数据域为7的节点不再是链表的节点。

    示意图如下:

    图8:insert-2

    还要额外考虑两种情况:

    1. 空链表时
    2. 插入位置超出链表节点的长度时
    3. 插入位置是链表的最后一个节点时,需要移动tail

    当是在链表最后一个节点插入时,示意图如下:

     图9:insert-3

    要在指定的索引位置插入一个节点,前提是需要找到这个位置,在链表中只有采用遍历的方式,具有O(n)的速度,最糟糕时会遍历链表的所有节点,而当找到插入点时,我们并不需要当前节点的信息,而是需要前一个节点的信息,所以代码中巧妙的使用了while cur_idx < idx-1:的方式,这样能使用cur这个变量能指向插入点上一个节点对象。

    实现insert方法的代码如下:

     1     def insert(self, idx, value):
     2         cur = self.head
     3         cur_idx = 0
     4         if cur is None:
     5             raise Exception('The list is an empty list')
     6         while cur_idx < idx - 1:
     7             cur = cur.next
     8             if cur is None:
     9                 raise Exception('list length less than index')
    10             cur_idx += 1
    11         node = Node(value)
    12         node.next = cur.next
    13         cur.next = node
    14         if node.next is None:
    15             self.tail = node

    remove方法实现

    remove方法接收一个idx参数,表示要删除节点的索引,此方法要考虑以下几种情况:

    1. 空链表,直接抛出异常
    2. 删除第一个节点时,移动head到删除节点的next指针指向的对象
    3. 链表只有一个节点时,把head与tail都指向None即可
    4. 删除最后一个节点时,需要移动tail到上一个节点
    5. 遍历链表时要判断给定的索引是否大于链表的长度,如果大于则抛出异常信息

    请看下边图例:

    图10:remove-1

     图11:remove-2

     以下为remove函数的代码:

     1     def remove(self, idx):
     2         cur = self.head
     3         cur_idx = 0
     4         if self.head is None:  # 空链表时
     5             raise Exception('The list is an empty list')
     6         while cur_idx < idx - 1:
     7             cur = cur.next
     8             if cur is None:
     9                 raise Exception('list length less than index')
    10             cur_idx += 1
    11         if idx == 0:  # 当删除第一个节点时
    12             self.head = cur.next
    13             cur = cur.next
    14             return
    15         if self.head is self.tail:  # 当只有一个节点的链表时
    16             self.head = None
    17             self.tail = None
    18             return
    19         cur.next = cur.next.next
    20         if cur.next is None:  # 当删除的节点是链表最后一个节点时
    21             self.tail = cur

    size函数实现

    size函数不接收参数,返回链表中节点的个数,要获得链表的节点个数,必定会遍历链表,直到最后一个节点的next指针指向None时链表遍历完成,遍历时可以用一个累加器来计算节点的个数,如下代码:

    1     def size(self):
    2         current = self.head
    3         count = 0
    4         if current is None:
    5             return 'The list is an empty list'
    6         while current is not None:
    7             count += 1
    8             current = current.next
    9         return count

    search函数实现

    search函数接收一个item参数,表示查找节点中数据域的值。search函数遍历链表,每到一个节点把当前节点的data值与item作比较,最糟糕的情况下会全遍历链表。如果查找到有些数据则返回True,否则返回False,代码如下:

    1     def search(self, item):
    2         current = self.head
    3         found = False
    4         while current is not None and not found:
    5             if current.data == item:
    6                 found = True
    7             else:
    8                 current = current.next
    9         return found

    Node类与LinkedList类完整代码

    最后把Node类和LinkerList类的完整代码整理如下:

    Node类:

    1 class Node:
    2     def __init__(self, data):
    3         self.data = data
    4         self.next = None

    LinkedList类及调度代码:

     1 class LinkedList:
     2     def __init__(self):
     3         self.head = None
     4         self.tail = None
     5   
     6     def is_empty(self):
     7         return self.head is None
     8   
     9     def append(self, data):
    10         node = Node(data)
    11         if self.head is None:
    12             self.head = node
    13             self.tail = node
    14         else:
    15             self.tail.next = node
    16             self.tail = node
    17   
    18     def iter(self):
    19         if not self.head:
    20             return
    21         cur = self.head
    22         yield cur.data
    23         while cur.next:
    24             cur = cur.next
    25             yield cur.data
    26   
    27     def insert(self, idx, value):
    28         cur = self.head
    29         cur_idx = 0
    30         if cur is None:             # 判断是否是空链表
    31             raise Exception('The list is an empty list')
    32         while cur_idx < idx-1:   # 遍历链表
    33             cur = cur.next
    34             if cur is None:   # 判断是不是最后一个元素
    35                 raise Exception('list length less than index')
    36             cur_idx += 1
    37         node = Node(value)
    38         node.next = cur.next
    39         cur.next = node
    40         if node.next is None:
    41             self.tail = node
    42   
    43     def remove(self, idx):
    44         cur = self.head
    45         cur_idx = 0
    46         if self.head is None:  # 空链表时
    47             raise Exception('The list is an empty list')
    48         while cur_idx < idx-1:
    49             cur = cur.next
    50             if cur is None:
    51                 raise Exception('list length less than index')
    52             cur_idx += 1
    53         if idx == 0:   # 当删除第一个节点时
    54             self.head = cur.next
    55             cur = cur.next
    56             return
    57         if self.head is self.tail:   # 当只有一个节点的链表时
    58             self.head = None
    59             self.tail = None
    60             return
    61         cur.next = cur.next.next
    62         if cur.next is None:  # 当删除的节点是链表最后一个节点时
    63             self.tail = cur
    64   
    65     def size(self):
    66         current = self.head
    67         count = 0
    68         if current is None:
    69             return 'The list is an empty list'
    70         while current is not None:
    71             count += 1
    72             current = current.next
    73         return count
    74   
    75     def search(self, item):
    76         current = self.head
    77         found = False
    78         while current is not None and not found:
    79             if current.data == item:
    80                 found = True
    81             else:
    82                 current = current.next
    83         return found
    84   
    85 if __name__ == '__main__':
    86     link_list = LinkedList()
    87     for i in range(150):
    88         link_list.append(i)
    89 #    print(link_list.is_empty())
    90 #    link_list.insert(10, 30)
    91   
    92 #    link_list.remove(0)
    93   
    94     for node in link_list.iter():
    95         print('node is {0}'.format(node))
    96     print(link_list.size())
    97 #    print(link_list.search(20))
  • 相关阅读:
    一枚渣硕的2019校招记录
    PLT hook笔记
    从排序数组中删除重复项
    golang刷Leetcode系列 --- 实现strStr()
    Docker镜像浅谈
    golang刷Leetcode系列 --- 加1
    ubuntu包管理机制
    LeetCode 234——回文链表
    LeetCode 19——删除链表的倒数第N个节点(JAVA)
    LeetCode 160——相交链表(JAVA)
  • 原文地址:https://www.cnblogs.com/chenshengkai/p/12090105.html
Copyright © 2020-2023  润新知