算法学习【第三篇】:链表
链表
为什么需要链表 顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以 使用起来并不是很灵活。 链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。 链表的定义 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是 在每一个节点(数据存储单元)里存放下一个节点的位置信息(即地址)

单向链表
单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一 个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值

·表元素域elem用来存放具体的数据。 ·链接域next用来存放下一个节点的位置(python中的标识) ·变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点。
节点实现
class SingleNode(object): ““单链表的结点" def_init_(self,item): #_item存放数据元素 self.item=item #_next是下一个节点的标识 self.next=None
单链表的操作(其他链表大致相同)
·is_empty0链表是否为空 ·length0链表长度 ·travel0遍历整个链表 ·add(item)链表头部添加元素 ·append(item)链表尾部添加元素 ·insert(pos,item)指定位置添加元素 ·remove(item)删除节点 ·search(item)查找节点是否存在
单链表的实现
# coding:utf-8
class Node(object):
"""节点Node"""
def __init__(self, elem):
self.elem = elem
self.next = None
class SingleLinkList(object):
"""单链表"""
def __init__(self, node=None):
self.__head = None
def is_empty(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_empty():
self.__head = node
else:
cur = self.__head
while cur.next != None:
cur = cur.next
cur.next = node
def insert(self, pos, item):
'''指定位置添加元素
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
# 当循环退出后,pre指向pos-1位置
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
break
else:
pre = cur
cur = cur.next
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__':
l1 = SingleLinkList()
print(l1.is_empty())
print(l1.length())
l1.append(1)
print(l1.is_empty())
print(l1.length())
l1.append(2)
l1.add(8)
l1.append(3)
l1.append(4)
l1.append(5)
l1.append(6)
l1.insert(-1, 9)
l1.travel()
l1.insert(3, 100)
l1.travel()
l1.insert(10, 200)
l1.travel()
l1.remove(100)
l1.travel()
l1.remove(9)
l1.travel()
l1.remove(200)
l1.travel()
'''
True
0
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,而是指向链表的头节点

# coding:utf-8
class Node(object):
"""结点Node"""
def __init__(self, elem):
self.elem = elem
self.next = None
self.prev = None
class DoubuleLinkList(object):
"""单向循环链表"""
def __init__(self, node=None):
self.__head = node
if node:
node.next = node
def is_empty(self):
'''链表是否为空'''
return self.__head == None
def length(self):
'''链表长度'''
if self.is_empty():
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_empty():
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)
if self.is_empty():
self.__head = node
node.next = node
else:
cur = self.__head
while cur.next != self.__head:
cur = cur.next
# 退出循环,cur指向尾结点
node.next = self.__head
self.__head = node
# cur.next = node
cur.next = self.__head
def append(self, item):
'''链表尾部添加元素,尾插法'''
node = Node(item)
if self.is_empty():
self.__head = node
node.next = node
else:
cur = self.__head
while cur.next != self.__head:
cur = cur.next
# node.next=cur.next
node.next = self.__head
cur.next = node
def insert(self, pos, item):
'''指定位置添加元素
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
# 当前循环退出后,pre指向pos-1位置
node = Node(item)
node.next = pre.next
pre.next = node
def remove(self, item):
'''删除节点'''
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
pre.next = self.__head
def search(self, item):
'''查找节点是否存在'''
if self.is_empty():
return False
cur = self.__head
while cur.next != self.__head:
if cur.elem == item:
return True
else:
cur = cur.next
# 退出循环,cur指向尾结点
if cur.elem == item:
return True
return False
if __name__ == '__main__':
l1 = DoubuleLinkList()
print(l1.is_empty())
print(l1.length())
l1.append(1)
print(l1.is_empty())
print(l1.length())
l1.append(2)
l1.add(8)
l1.append(3)
l1.append(4)
l1.append(5)
l1.append(6)
l1.insert(-1, 9)
l1.travel()
l1.insert(3, 100)
l1.travel()
l1.insert(10, 200)
l1.travel()
l1.remove(100)
l1.travel()
l1.remove(9)
l1.travel()
l1.remove(200)
l1.travel()
'''
True
0
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
'''
双向链表
一种更复杂的链表是“双向链表”或“双面链表”。每个节点有两个链接:一个指向前一个节点,当此节点为第
一个节点时,指向空值;而另一个指向下一个节点,当此节点为最后一个节点时,指向空值

# coding:utf-8
class Node(object):
"""结点Node"""
def __init__(self, elem):
self.elem = elem
self.next = None
self.prev=None
class DoubuleLinkList(object):
"""双链表"""
def __init__(self, node=None):
self.__head = None
def is_empty(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
node.next.prev=node
def append(self,item):
'''链表尾部添加元素,尾插法'''
node = Node(item)
if self.is_empty():
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):
'''指定位置添加元素
pos 从0开始
'''
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
# 当循环退出后,cur指向pos位置
node = Node(item)
node.next=cur
node.prev=cur.prev
cur.prev.next=node
cur.prev=node
def remove(self, item):
'''删除节点'''
cur = self.__head
while cur != None:
if cur.elem == item:
# 先判断是否是头节点
# 头节点
if cur == self.__head:
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
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__':
l1 = DoubuleLinkList()
print(l1.is_empty())
print(l1.length())
l1.append(1)
print(l1.is_empty())
print(l1.length())
l1.append(2)
l1.add(8)
l1.append(3)
l1.append(4)
l1.append(5)
l1.append(6)
l1.insert(-1, 9)
l1.travel()
l1.insert(3, 100)
l1.travel()
l1.insert(10, 200)
l1.travel()
l1.remove(100)
l1.travel()
l1.remove(9)
l1.travel()
l1.remove(200)
l1.travel()
'''
True
0
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
'''
双向循环链表
# 链表的节点
class Node(object):
def __init__(self, item):
self.item = item # 节点数值
self.prev = None # 用于指向前一个元素
self.next = None # 用于指向后一个元素
# 双向循环链表
class DoubleCircleLinkList(object):
def __init__(self):
self.__head = None # 初始化的时候头节点设为空、
# 判断链表是否为空,head为None 的话则链表是空的
def is_empty(self):
return self.__head is None
# 头部添加元素的方法
def add(self, item):
node = Node(item) # 新建一个节点node 里面的值是item
# 如果链表是空的,则node的next和prev都指向自己(因为是双向循环),head指向node
if self.is_empty():
self.__head = node
node.next = node
node.prev = node
# 否则链表不空
else:
node.next = self.__head # node的next设为现在的head
node.prev = self.__head.prev # node的prev 设为现在head的prev
self.__head.prev.next = node # 现在head的前一个元素的next设为node
self.__head.prev = node # 现在head的前驱 改为node
self.__head = node # 更改头部指针
# 尾部添加元素方法
def append(self, item):
# 如果当前链表是空的 那就调用头部插入方法
if self.is_empty():
self.add(item)
# 否则链表不为空
else:
node = Node(item) # 新建一个节点node
# 因为是双向循环链表,所以head的prev其实就是链表的尾部
node.next = self.__head # node的下一个设为头
node.prev = self.__head.prev # node的前驱设为现在头部的前驱
self.__head.prev.next = node # 头部前驱的后继设为node
self.__head.prev = node # 头部自己的前驱改为node
# 获得链表长度 节点个数
def length(self):
# 如果链表是空的 就返回0
if self.is_empty():
return 0
# 如果不是空的
else:
cur = self.__head # 临时变量cur表示当前位置 初始化设为头head
count = 1 # 设一个计数器count,cur每指向一个节点,count就自增1 目前cur指向头,所以count初始化为1
# 如果cur.next不是head,说明cur目前不是最后一个元素,那么count就1,再让cur后移一位
while cur.next is not self.__head:
count += 1
cur = cur.next
# 跳出循环说明所有元素都被累加了一次 返回count就是一共有多少个元素
return count
# 遍历链表的功能
def travel(self):
# 如果当前自己是空的,那就不遍历
if self.is_empty():
return
# 链表不空
else:
cur = self.__head # 临时变量cur表示当前位置,初始化为链表的头部
# 只要cur的后继不是头说明cur不是最后一个节点,我们就输出当前值,并让cur后移一个节点
while cur.next is not self.__head:
print(cur.item, end=" ")
cur = cur.next
# 当cur的后继是head的时候跳出循环了,最后一个节点还没有打印值 在这里打印出来
print(cur.item)
# 置顶位置插入节点
def insert(self, pos, item):
# 如果位置<=0 则调用头部插入方法
if pos <= 0:
self.add(item)
# 如果位置是最后一个或者更大 就调用尾部插入方法
elif pos > self.length() - 1:
self.append(item)
# 否则插入位置就是链表中间
else:
index = 0 # 设置计数器,用于标记我们后移了多少步
cur = self.__head # cur标记当前所在位置
# 让index每次自增1 ,cur后移,当index=pos-1的时候说明cur在要插入位置的前一个元素,这时候停下
while index < pos - 1:
index += 1
cur = cur.next
# 跳出循环,cur在要插入位置的前一个元素,将node插入到cur的后面
node = Node(item) # 新建一个节点
node.next = cur.next # node的后继设为cur的后继
node.prev = cur # node的前驱设为cur
cur.next.prev = node # cur后继的前驱改为node
cur.next = node # cur后继改为node
# 删除节点操作
def remove(self, item):
# 如果链表为空 直接不操作
if self.is_empty():
return
# 链表不为空
else:
cur = self.__head # 临时变量标记位置,从头开始
# 如果头结点就是 要删除的元素
if cur.item == item:
# 如果只有一个节点 链表就空了 head设为None
if self.length() == 1:
self.__head = None
# 如果多个元素
else:
self.__head = cur.next # 头指针指向cur的下一个
cur.next.prev = cur.prev # cur后继的前驱改为cur的前驱
cur.prev.next = cur.next # cur前驱的后继改为cur的后继
# 否则 头节点不是要删除的节点 我们要向下遍历
else:
cur = cur.next # 把cur后移一个节点
# 循环让cur后移一直到链表尾元素位置,期间如果找得到就删除节点,找不到就跳出循环,
while cur is not self.__head:
# 找到了元素cur就是要删除的
if cur.item == item:
cur.prev.next = cur.next # cur的前驱的后继改为cur的后继
cur.next.prev = cur.prev # cur的后继的前驱改为cur的前驱
cur = cur.next
# 搜索节点是否存在
def search(self, item):
# 如果链表是空的一定不存在
if self.is_empty():
return False
# 否则链表不空
else:
cur = self.__head # 设置临时cur从头开始
# cur不断后移,一直到尾节点为止
while cur.next is not self.__head:
# 如果期间找到了就返回一个True 结束运行
if cur.item == item:
return True
cur = cur.next
# 从循环跳出来cur就指向了尾元素 看一下为元素是不是要找的 是就返回True
if cur.item == item:
return True
# 所有元素都不是 就返回False 没找到
return False
链表与顺序表的对比
链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的
使用要相对灵活。
链表与顺序表的各种操作复杂度如下所示:

注意虽然表面看起来复杂度都是O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作。链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是O(1)。顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前 后移位操作,只能通过拷贝和覆盖的方法进行。

浙公网安备 33010602011771号