单向链表
链表是实现了数据之间保持逻辑顺序,但存储空间不必按顺序存放的方法。可以用一个图来表示这种链表的数据结构:
链表中的基本要素:
1. 结点(也可以叫节点或元素),每一个结点有两个域,左边部份叫`值域`,用于存放用户数据;右边叫`指针域`,一般是存储着到下一个元素的指针 2. head结点,head是一个特殊的结节,head结点永远指向第一个结点 3. tail结点,tail结点也是一个特殊的结点,tail结点永远指向最后一个节点 4. None或Null,链表中最后一个结点指针域的指针指向None值,因也叫接地点,所以有些资料上用电气上的接地符号代表None
链表的实现完全可以不使用内置的数据结构来存放node数据,但为了简化逻辑以下代码使用list来存放node节点。
# 单向链表
class SingleNode:
"""
节点类,存储节点值及指向下一个节点的指针
"""
def __init__(self, value):
self.value = value
self.next = None
def __repr__(self):
return str(self.value)
class LinkedList:
def __init__(self):
self.nodes = [] # 用于存放节点,使用list检索方便,但是插入,remove不合适
self.head = None
self.tail = None
def is_empty(self):
# 判断链表是否为空
return self.nodes is None
def size(self):
# 获取链表长度
return len(self.nodes)
def append(self, value):
"""
1. 链表为空时,直接把head指向node
2. 链表不为空时,把tail指向节点的next指向node
"""
node = SingleNode(value)
prev = self.tail # 取得tail指向的节点
if prev is None: # 说明此时是一个空的链表
self.head = node
self.tail = node # tail必定指向node
else:
prev.next = node
self.tail = node # tail必定指向node
self.nodes.append(node)
# print(self.nodes)
def iter_nodes(self):
"""
遍历节点
"""
current = self.head
while current:
yield current
current = current.next
def iter_nodes2(self):
for i in self.nodes: # 可借助list遍历
yield i.value
def search(self, value):
# 判断value是否在链表中的值
return value in (i.value for i in self.nodes)
def insert(self, idx, value):
# 插入节点,为空链表或idx越界时
if self.head is None or idx + 1 > len(self.nodes):
self.append(value)
else:
pre_node = self.nodes[idx]
cur_node = SingleNode(value)
self.nodes.insert(idx, cur_node)
self.head = cur_node
cur_node.next = pre_node
def remove(self, idx): # 只能以索引的方式删除,索引是唯一的,面节点的value可以重复
if self.head is None: # 空链表
raise Exception('The linked list is an empty')
elif idx > len(self.nodes) - 1: # idx 越界
raise Exception('Linked list length less than index')
elif self.head == self.tail: # 链表中只有一个node,直接删除
self.head = None
self.tail = None
return self.nodes.pop()
elif idx == 0: # 删除第一个node
cur = self.head
self.head = cur.next
return self.nodes.pop(0)
elif idx == len(self.nodes) - 1: # 删除最后一个node
tail = self.nodes[-2]
tail.next = None
self.tail = tail
return self.nodes.pop()
else:
pre = self.nodes[idx-1]
pre.next = self.nodes[idx + 1]
return self.nodes.pop(idx)
# 以下为链表的测试
ll = LinkedList()
ll.append(2)
ll.append(20)
ll.append(6)
inodes = ll.iter_nodes()
print(inodes)
for note in inodes:
print(note.value)
print('='*20)
print(list(ll.iter_nodes2()))
ll.insert(2, 10)
print(ll.nodes)
print(ll.is_empty())
print('='*20)
print(ll.remove(1))
print(list(ll.iter_nodes2()))
print(ll.size())
print(ll.search(200))
# 输出
<generator object LinkedList.iter_nodes at 0x100a0e6d0>
2
20
6
====================
[2, 20, 6]
[2, 20, 10, 6]
False
====================
20
[2, 10, 6]
3
False
引入list存放node后就把问题简单化了,本是无序的链表节点存放在list后就成了有序的了,list的引入主要是简化了 insert 和 remove 的操作,如果不引入list,那插入操作和删除操作都只能遍历链表找到相应的位置后把node插入,而引入list后就可以利用list的索引完成node的插入和删除。如果有大量的插入和删除操作,引入list不是很好的选择,这是因为在数据规模较大时,list的insert和remove开销较大。
双向链表
双向链表是在单向链表的node上增加了一个指针域。双向链表中每个节点有三部分组成,两个指针域和一个值域,第一个指针指向上一个节点,第二个指针指向下一个节点,链表中第一个节点的第一个指针指向None或Null,最后一个节点的第二个指针指向None或Null。如下图:
代码实现:
class SingleNode:
def __init__(self, value):
self.value = value
self.prev = None
self.next = None
def __repr__(self):
return str(self.value)
class LinkedList:
def __init__(self):
self.head = None
self.tail = None
def append(self, value):
node = SingleNode(value)
if self.head is None: # 为空链表时
self.head = node
else: # 为非空链表时
self.tail.next = node # 1. 最后一个node的next指向加入的node
node.prev = self.tail # 2. 新加入node的prev指向加入node前最后的节点,即self.tail
self.tail = node # 不管链表为空或非空,最终self.tail始终都会指向append进来的node
def iter_nodes(self, reverse=False): # 双向链表可以反向迭代
current = self.tail if reverse else self.head
while current:
yield current
current = current.prev if reverse else current.next
def pop(self):
# 1. 为空链表时
if self.head is None:
raise Exception('Empty')
# 获取一些基础信息
tail = self.tail
prev = tail.prev
# next = tail.next
# 2. 只有一个节点的链表时
if prev is None:
self.head = None
self.tail = None
return tail.value
else: # 3. 链表中节点数大于1时
self.tail = prev # 把链表的tail指向上一个节点
prev.next = None # 修正前一个node的next
return tail.value
def get_item(self, index):
if index < 0:
return None
current = None
for i, n in enumerate(self.iter_nodes()):
if index == i:
current = n
break
if current is not None:
return current # 返回节点
def insert(self, index, value):
if index < 0:
raise Exception('Error')
current = None
for i, n in enumerate(self.iter_nodes()):
if index == i:
current = n
break
if current is None: # 通过上边的for循环迭代后没有找到对应索引的node时,直接调用append函数在尾部追加
self.append(value)
return None
prev = current.prev
# next = current.next
node = SingleNode(value)
if prev is None: # 原链表里只有一个元素,此时就在头部插入
self.head = node
current.prev = node
node.next = current
else: # 在链表中间插入
node.prev = prev # 新节点的prev指向上一个节点
node.next = current # 新节点的next指向for循环找到的节点
prev.next = node # for循环找到的节点的上一个节点的next指向新节点
current.prev = node # for循环找到的节点的上一个节点指向新节点
def remove(self, index): # 使用索引,因值可能会重复
# 需要考虑的情况:
# 1.空链表
# 2. 只有一个节点的链表
# 3. 删除的是链表中第一个节点
# 3. 删除的是链表中最后一个节点
# 4. 索引超出界限
if self.head is None:
raise Exception('Empty')
if index < 0:
raise ValueError('Wrong Index {}'.format(index))
current = None
for i, n in enumerate(self.iter_nodes()):
if index == i:
current = n
break
if current is None: # 索引超出界限
raise ValueError('Wrong Index {}, Out of boundary'.format(index))
prev = current.prev
next = current.next
if prev is None and next is None: # 链表仅有一个节点时
self.head = None
self.tail = None
elif prev is None: # 删除链表中第一个节点时
self.head = next
next.prev = None
elif next is None: # 删除链表中最后一个节点时
self.tail = prev
prev.next = None
else: # 其他情况,剩下删除链表中的中间节点
prev.next = next
next.prev = prev
del current # 直接清理掉节点
if __name__ == '__main__':
ll = LinkedList()
ll.append('aaa')
ll.append(1)
# ll.append(2)
# ll.append(3)
# ll.append(4)
# ll.append('x')
# ll.append('y')
# for node in ll.iter_nodes():
# print(node.value)
# ll.pop()
# ll.pop()
# ll.pop()
print('==========')
ll.insert(0, 'bbb')
# ll.insert(6, 'z')
# ll.insert(30, 'xxxxxxxx')
for node in ll.iter_nodes():
print(node.value)
print('='*20)
ll.remove(0)
for node in ll.iter_nodes():
print(node.value)
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did169352