链表定义:将元素存放在通过连接构造起来的一系列存储块中
一、单链表
定义:每个节点两部分,即元素区elem和链接区next(指向下一个节点的地址)
-
第一个节点叫头结点
-
最后一个节点叫尾结点
-
尾结点的链接区指向空
# 创建一个节点类
class Node():
def __init__(self, elem, next=None):
self.elem = elem
self.next = next
# 创建一个单链表类
class SingleLinkList():
def __init__(self, node=None):
# 定义私有属性头结点
self.__head = node
def is_empty(self):
# 判断列表是否为空,只有头结点指向None则为空
return self.__head == None
def travel(self):
# 遍历整个链表
cur = self.__head
while cur != None:
print(cur.elem, end=' ')
cur = cur.next
print()
def length(self):
# 链表长度,需要遍历,只要找到尾节点
cur = self.__head # cur游标,用来移动遍历节点,表示当前节点
count = 0 # 负责计数
while cur != None:
count += 1
cur = cur.next
return count
def add(self, elem):
# 头部添加元素,头插法
first = self.__head
node = Node(elem, next=first)
self.__head = node
def append(self, elem):
# 尾部添加元素,尾插法
node = Node(elem)
cur = self.__head
if self.is_empty():
self.__head = node
else:
while cur.next != None:
cur = cur.next
cur.next = node
def insert(self, pos, elem):
# 指定位置插入元素
if pos <= 0:
# 头插法
self.add(elem)
elif pos > self.length() - 1:
# 尾插法
self.append(elem)
else:
pre = self.__head
count = 0
while count < pos - 1:
pre = pre.next
count += 1
# 当循环退出后,pre指向pos-1的位置
node = Node(elem)
node.next = pre.next
pre.next = node
def search(self, elem):
# 求节点是否存在
cur = self.__head
while cur != None:
if cur.elem == elem:
return True
else:
cur = cur.next
return False
def remove(self, elem):
# 根据数据删除遇到的第一个节点
cur = self.__head
pre = None
while cur != None:
# 判断该元素是否为删除的元素
if cur.elem == elem:
# 先判断此节点是否为头结点
if cur == self.__head:
self.__head = cur.next
# 不是头结点的话,其他普通情况和只有一个节点的情况都符合
else:
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
if __name__ == '__main__':
s = SingleLinkList()
s.add(1)
s.remove(1)
s.travel()
注意:关于链表的增加,记住一个原则:为了避免丢失,先不要改变原链表顺序,先让插入的元素的后指针指向后边的元素,再让插入前的元素指向新元素,再断开原来的结点
二、双向链表
定义:在单链表保存下一个节点位置的特性处,再保存上一个节点位置。
- 当前节点的前一个节点叫前驱结点
- 当前节点的后一个节点叫后继结点
- 头结点的前驱为空,尾结点的后继也为空
# 创建一个节点类
class Node():
def __init__(self, elem, prev=None, next=None):
self.elem = elem
self.next = next
self.prev = prev
# 创建一个双链表类
class DoubleLinkList():
def __init__(self, node=None):
# 定义私有属性头结点
self.__head = node
def is_empty(self):
# 判断链表是否为空,只有头结点指向None则为空
return self.__head == None
def travel(self):
# 遍历整个链表
cur = self.__head
while cur != None:
print(cur.elem, end=' ')
cur = cur.next
print()
def length(self):
# 链表长度,需要遍历,只要找到尾节点
cur = self.__head # cur游标,用来移动遍历节点,表示当前节点
count = 0 # 负责计数
while cur != None:
count += 1
cur = cur.next
return count
def add(self, elem):
# 头部添加元素,头插法
node = Node(elem)
if self.is_empty():
self.__head = node
else:
node.next = self.__head
self.__head = node
# 若不是空链表,原首节点的prev指向新添加的节点
node.next.prev = node
def append(self, elem):
# 尾部添加元素,尾插法
node = Node(elem)
cur = self.__head
if self.is_empty():
self.__head = node
else:
while cur.next != None:
cur = cur.next
cur.next = node
node.prev = cur
def insert(self, pos, elem):
# 指定位置插入元素
if pos <= 0:
# 头插法
self.add(elem)
elif pos > self.length() - 1:
# 尾插法
self.append(elem)
else:
count = 0
cur = self.__head
while count < pos:
cur = cur.next
count += 1
# 当循环退出后,cur指向的是原位置的结点
node = Node(elem)
node.next = cur
node.prev = cur.prev
cur.prev.next = node
cur.prev = node
def search(self, elem):
# 求节点是否存在
cur = self.__head
while cur != None:
if cur.elem == elem:
return True
else:
cur = cur.next
return False
def remove(self, elem):
# 根据数据删除遇到的第一个节点
cur = self.__head
while cur != None:
# 判断该元素是否为删除的元素
if cur.elem == elem:
# 先判断此节点是否为头结点
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__':
d = DoubleLinkList()
d.append(1)
d.append(2)
d.travel()
d.remove(2)
d.travel()
三、单双链表的改变处对比
1.头插法和尾插法
2.随机插入
3.删除
四、链表与顺序表的对比
-
链表:
- ①失去了顺序表随机读取的优点
- ②增加了结点的指针域,空间开销较大
- ③对存储空间使用比较灵活
-
关于操作
链表 顺序表 访问元素 O(n) O(1) 在头部插入/删除 O(1) O(n) 在尾部插入/删除 O(n) O(1) 在中间插入/删除 O(n) O(n) 注意虽然表面复杂度看起来都是O(n),但是链表和顺序表插入和删除是完全不同的操作。链表主要耗时在遍历,删除和插入本身都是O(1)。顺序表查找很快,耗时在拷贝覆盖,因为除了目标在尾部的特殊操作,其他情况都需要将其他元素进行移位操作,只能通过拷贝和覆盖方法进行。