链表
为什么要用链表
顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以使用起来并不是很灵活。
链表结构可以充分利用计算机内存,实现灵活的内存动态管理。
链表的定义
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但时不想顺序表一样连续存储数据,而是在每个节点里存放下一个节点的位置信息(即地址)。
单向链表
单项链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表的下一个节点,而最后一个节点的链接域则指向一个空值。
- 表元素域elem用来存放具体的数据。
- 链接域next用来存放下一个节点的位置
- 变量p指向链表的头节点的位置,从p出发能找到表中的任意节点。
节点实现
class SingleNode:
'''单链表的节点'''
def __init__(self, item):
# 存放item
self.item = item
# __next是下一个节点的标识
self.__next = None
单链表的操作
- is_empty()链表是否为空
- length()链表的长度
- travel()遍历整个链表
- add(item)链表头部添加元素
- append(item)链表尾部添加元素
- insert(pos,item)指定位置添加元素
- remove(item)删除节点
- search(item)查找节点是否存在
单链表的实现
class Node:
'''单链表的节点'''
def __init__(self, item):
# 存放item
self.elem = item
# __next是下一个节点的标识
self.next = None
class SingleLinkList():
def __init__(self, node=None):
self.__head = node
def is_enmpty(self):
'''链表是否为空'''
return self.__head == None
def length(self):
'''链表长度'''
# cur游标,用来移动遍历节点
cur = self.__head
# count记录数据
count = 0
while cur != None:
count += 1
cur = cur.next
return count
def travel(self):
'''遍历链表'''
cur = self.__head
while cur != None:
print(cur.elem, end=' ')
cur = cur.next
print('')
def add(self, item):
'''链表头部插入法,头插法'''
node = Node(item)
node.next = self.__head
self.__head = node
def append(self, item):
'''链表尾部添加元素, 尾插法'''
node = Node(item)
if self.is_enmpty():
self.__head = node
else:
cur = self.__head
while cur.next != None:
cur = cur.next
cur.next = node
def insert(self, pos, item):
'''指定位置添加元素
:param pos 从0开始
'''
if pos <= 0:
self.add(item)
elif pos > (self.length() - 1):
self.append(item)
else:
pre = self.__head
count = 0
while count < (pos - 1):
count += 1
pre = pre.next
node = Node(item)
node.next = pre.next
pre.next = node
def remove(self, item):
'''删除节点,使用两个游标'''
cur =self.__head
pre =None
while cur != None:
if cur.elem == item:
if cur == self.__head:
self.__head = cur.next
else:
pre.next = cur.next
return True
else:
pre = cur
cur = cur.next
return False
def search(self, item):
'''查找链表元素'''
cur = self.__head
while cur != None:
if cur.elem == item:
return True
else:
cur = cur.next
return False
if __name__ == '__main__':
ll = SingleLinkList()
print(ll.is_enmpty())
print(ll.length)
ll.append(1)
print(ll.is_enmpty())
print(ll.length())
ll.append((2))
ll.add(8)
ll.append((3))
ll.append((4))
ll.append((5))
ll.append((6))
ll.insert(-1, 9)
ll.travel()
ll.insert(3, 100)
ll.travel()
ll.insert(10, 200)
ll.travel()
ll.remove(100)
ll.travel()
ll.remove(9)
ll.travel()
ll.remove(200)
ll.travel()
上面程序的输出为:
False
1
9 8 1 2 3 4 5 6
9 8 1 100 2 3 4 5 6
9 8 1 100 2 3 4 5 6 200
9 8 1 2 3 4 5 6 200
8 1 2 3 4 5 6 200
8 1 2 3 4 5 6
链表域顺序表的对比
链表失去了顺序表随机读取的优点,同时链表由于增加了节点的指针域,空间开销比较大,但对于存储空间的使用要相对灵活。
链表与顺序表的各种操作复杂度如下:
操作 | 链表 | 顺序表 |
---|---|---|
访问元素 | O(n) | O(1) |
在头部插入/删除 | O(1) | O(n) |
在尾部插入/删除 | O(n) | O(1) |
在中间插入/删除 | O(n) | O(n) |
注意虽然表面看起来复杂度都是O(n),但是链表和顺序表在插入和删除时进行的时完全不同的操作。链表的主要耗时操作时遍历查找,删除和插入操作本身的复杂度都是O(1)。顺序表查找很快,主要耗时的操作时拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前后以为操作,只能通过拷贝和覆盖的方法进行。
双向链表
一种更复杂的链表时“双向链表”或“双面链表”。每个节点有两个链表:一个指向前一个节点,档次节点为第一个节点时,之前空值;而另一个指向下一个节点,当此节点为最后一个节点时,指向空值。
操作
- is_empty()链表是否为空
- length()链表的长度
- travel()遍历整个链表
- add(item)链表头部添加元素
- append(item)链表尾部添加元素
- insert(pos,item)指定位置添加元素
- remove(item)删除节点
- search(item)查找节点是否存在
双向链表的实现
class Node:
'''单链表的节点'''
def __init__(self, item):
# 存放item
self.elem = item
# __next是下一个节点的标识
self.next = None
self.prev = None
class DoubleLinkList(SingleLinkList):
def __init__(self, node=None):
self.__head = node
def is_enmpty(self):
'''链表是否为空'''
return self.__head == None
def length(self):
'''链表长度'''
# cur游标,用来移动遍历节点
cur = self.__head
# count记录数据
count = 0
while cur != None:
count += 1
cur = cur.next
return count
def travel(self):
'''遍历链表'''
cur = self.__head
while cur != None:
print(cur.elem, end=' ')
cur = cur.next
print('')
def search(self, item):
'''查找链表元素'''
cur = self.__head
while cur != None:
if cur.elem == item:
return True
else:
cur = cur.next
return False
'''is_empty search length travel 不用改动'''
def add(self, item):
'''链表头部添加元素'''
node = Node(item)
node.next = self.__head
self.__head.prev = node
self.__head = node
def append(self, item):
'''链表尾部插入元素'''
node = Node(item)
if self.__head == None:
self.__head = node
else:
cur = self.__head
while cur.next != None:
cur = cur.next
cur.next = node
node.prev = cur
def insert(self, pos, item):
if pos <= 0:
self.add(item)
elif pos > (self.length() - 1):
self.append(item)
else:
cur = self.__head
count = 0
while count < pos:
count += 1
cur = cur.next
node = Node(item)
node.next = cur
node.prev = cur.prev
cur.prev = node
node.prev.next = node
def remove(self, item):
cur = self.__head
while cur != None:
if cur.elem == item:
# 判断此节点是否是头节点
# 头节点
if cur == self.__head:
self.__head = cur.next
if cur.next:
#判断是否只有一个节点
cur.next.prev = None
else:
cur.prev.next = cur.next
if cur.next:
cur.next.prev = cur.prev
break
else:
cur = cur.next
if __name__ == '__main__':
ll = DoubleLinkList()
print(ll.is_enmpty())
print(ll.length)
ll.append(1)
print(ll.is_enmpty())
print(ll.length())
ll.append((2))
ll.add(8)
ll.append((3))
ll.append((4))
ll.append((5))
ll.append((6))
ll.insert(-1, 9)
ll.travel()
ll.insert(3, 100)
ll.travel()
ll.insert(10, 200)
ll.travel()
ll.remove(100)
ll.travel()
ll.remove(9)
ll.travel()
ll.remove(200)
ll.travel()
上面代码输出
False
1
9 8 1 2 3 4 5 6
9 8 1 100 2 3 4 5 6
9 8 1 100 2 3 4 5 6 200
9 8 1 2 3 4 5 6 200
8 1 2 3 4 5 6 200
8 1 2 3 4 5 6
单向循环链表
单链表的一个变型是单向循环链表,链表中最后一个节点的next域不再为None,而是指向链表的头节点。
操作
- is_empty()链表是否为空
- length()链表的长度
- travel()遍历整个链表
- add(item)链表头部添加元素
- append(item)链表尾部添加元素
- insert(pos,item)指定位置添加元素
- remove(item)删除节点
- search(item)查找节点是否存在
单向循环列表的实现
class Node:
'''单链表的节点'''
def __init__(self, item):
# 存放item
self.elem = item
# __next是下一个节点的标识
self.next = None
class SingleCLinkList():
def __init__(self, node=None):
self.__head = node
if node:
node.next = node
def is_enmpty(self):
'''链表是否为空'''
return self.__head == None
def length(self):
'''链表长度'''
if self.is_enmpty():
return 0
# cur游标,用来移动遍历节点
cur = self.__head
# count记录数据
count = 1
while cur.next != self.__head:
count += 1
cur = cur.next
return count
def travel(self):
'''遍历链表'''
if self.is_enmpty():
return
cur = self.__head
while cur.next != self.__head:
print(cur.elem, end=' ')
cur = cur.next
#退出循环,cur指向尾节点,但尾节点的元素未打印
print(cur.elem)
def add(self, item):
'''链表头部插入法,头插法'''
node = Node(item)
cur = self.__head
if self.is_enmpty():
self.__head =node
node.next = node
else:
while cur.next != self.__head:
cur = cur.next
node.next = self.__head
self.__head = node
#cur.next = node
cur.next = self.__head
def append(self, item):
'''链表尾部添加元素, 尾插法'''
node = Node(item)
if self.is_enmpty():
self.__head = node
node.next =node
else:
cur = self.__head
while cur.next != self.__head:
cur = cur.next
#node.next = cur.next
cur.next = node
node.next=self.__head
#不用改
def insert(self, pos, item):
'''指定位置添加元素
:param pos 从0开始
'''
if pos <= 0:
self.add(item)
elif pos > (self.length() - 1):
self.append(item)
else:
pre = self.__head
count = 0
while count < (pos - 1):
count += 1
pre = pre.next
node = Node(item)
node.next = pre.next
pre.next = node
def remove(self, item):
'''删除节点,使用两个游标'''
if self.is_enmpty():
return
cur = self.__head
pre = None
while cur.next != self.__head:
if cur.elem == item:
#先判断是否为头节点
if cur == self.__head:
# 头节点的情况,先寻找尾节点
rear = self.__head
while rear.next != self.__head:
rear = rear.next
self.__head = cur.next
rear.next = self.__head
else:
#中间节点
pre.next = cur.next
return
else:
pre = cur
cur = cur.next
#退出循环,cur指向尾节点
if cur.elem == item:
if cur == self.__head:
#链表只有一个节点
self.__head = None
else:
pre.next = cur.next
def search(self, item):
'''查找链表元素'''
cur = self.__head
while cur.next != self.__head:
if cur.elem == item:
return True
else:
cur = cur.next
if cur.elem == item:
return True
else:
return False
if __name__ == '__main__':
ll = SingleCLinkList()
print(ll.is_enmpty())
print(ll.length)
ll.append(1)
print(ll.is_enmpty())
print(ll.length())
ll.append((2))
ll.add(8)
ll.append((3))
ll.append((4))
ll.append((5))
ll.append((6))
ll.insert(-1, 9)
ll.travel()
ll.insert(3, 100)
ll.travel()
ll.insert(10, 200)
ll.travel()
ll.remove(100)
ll.travel()
ll.remove(9)
ll.travel()
ll.remove(200)
ll.travel()
上述代码的输出:
False
1
9 8 1 2 3 4 5 6
9 8 1 100 2 3 4 5 6
9 8 1 100 2 3 4 5 6 200
9 8 1 2 3 4 5 6 200
8 1 2 3 4 5 6 200
8 1 2 3 4 5 6