链表的定义
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是在每一个节点(数据存储单元)里存放下一个节点的位置信息(即地址)。
链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。
单向链表
单向链表定义
单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值。
- 表元素域elem用来存放具体的数据。
- 链接域next用来存放下一个节点的位置(python中的标识)
- 变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点。
单链表在Python中的实现
节点的构建
class Node(object):
def __init__(self,elem):
# elem是存放的实际值
self.elem = elem
# next指向下一节点,默认为None
self.next = None
单链表的构建、判断是否为空、计算长度、遍历链表
class SingleLinkList(object):
def __init__(self,node = None): # node = None相当于包含了建立一个空链表的情况
self.__head = node
def is_empty(self):
'''判断链表是否为空'''
return self.__head == None # return的值需要用print()打印出来
def length(self):
'''计算链表长度'''
cur = self.__head # 建立一个游标,首先指向头部
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
头部添加元素
def add(self, item):
'''链表头部添加元素:头插法'''
node = Node(item) # 先定义一个节点,把值放进去
# 注意先后顺序,否则后边的节点就找不到了
node.next = self.__head
self.__head = node
尾部添加元素
def append(self, item):
'''链表尾部添加元素:尾插法'''
node = Node(item)
cur = self.__head
if self.is_empty():
self.__head = node
else:
while cur.next != None:
cur = cur.next # 节点向下移动,直到最后一个节点,即cur.next == None
cur.next = node
指定位置添加元素
def insert(self,pos,item):
'''链表指定部分添加元素'''
node = Node(item)
count = 0
pre = self.__head
if pos <= 0:
self.add(item)
elif pos >= self.length()-1:
self.append(item)
else:
while count != pos-1:
pre = pre.next
count += 1
node.next = pre.next
pre.next = node
删除节点
def remove(self,item):
'''删除链表中的出现的第一个指定元素'''
cur = self.__head
pre = None #采用两个游标
while cur != None:
if cur.elem == item:
if pre == None: # 判断删除第一个节点的情况
self.__head = cur.next
else:
pre.next = cur.next
break #要退出循环 因为如果执行if中的语句就不会进入else的语句,即游标继续移动,所以要手动break
else: # 规定了两个游标的位置关系,同时使两个游标继续移动
pre = cur
cur = cur.next
查找节点是否存在
def search(self,item):
'''链表中元素是否存在,返回True或False'''
cur = self.__head
while cur != None:
if cur.elem == item:
return True
else:
cur = cur.next
return False
链表与顺序表的对比
链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的使用要相对灵活。
链表与顺序表的各种操作复杂度如下所示:
双向链表
双向链表的定义与结构
双向链表每个节点有两个链接,分别叫前驱和后继:一个指向前一个节点,当此节点为第一个节点时,指向空值;而另一个指向下一个节点,当此节点为最后一个节点时,指向空值。
双向链表的实现
节点的构建
相比于单向链表,多出一个prev属性
class Node(object):
def __init__(self,item):
self.elem = item
self.prev = None
self.next = None
双向链表的构建、判断是否为空、计算长度、遍历链表
因为这些功能并不涉及前驱链接,所以与单向链表相同,可以使用继承的方式,减少代码量。
class DoubleLinkList(object):
# 与单向链表相同的方法,可以使用继承
def __init__(self,node = None):
self.__head = node
def is_empty(self):
return self.__head is None # 尽量使用is
def length(self):
'''计算链表长度'''
cur = self.__head
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)
if self.__head is None:
self.__head = node
else:
node.next = self.__head
node.next.prev = node
self.__head = node # ※不需要再加node.prev = None,因为前面的设定中默认就是None
尾部添加元素
def append(self, item):
'''链表尾部添加元素:尾插法'''
node = Node(item)
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,item):
'''链表指定部分添加元素'''
node = Node(item)
count = 0
cur = self.__head
if pos <= 0:
self.add(item)
elif pos >= self.length()-1:
self.append(item)
else:
while count != pos-1:
cur = cur.next
count += 1
# 一定要画图去比较,根据断的先后顺序,代码不唯一
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: #当删除的节点位于第一个
self.__head = cur.next
if cur.next != None: # 补充链表中只有一个节点的情况
cur.next.prev = None # 还需要将下一个节点(这个节点仍存在)的prev置空
else:
cur.prev.next = cur.next
if cur.next: # 考虑删除最后一个节点的情况;※如果是最后一个节点,不需要将该节点的prev置空,因为这是要删除的节点
cur.next.prev = cur.prev
break #要退出循环 因为执行if后并没有进入else的语句,要手动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
单向循环链表
单向循环链表的定义与结构
单向链表的一个变形是单向循环链表,链表中最后一个节点的next域不再为None,而是指向链表的头节点。
单向循环链表的实现
节点的构建
节点的构建与单向链表相同。
class Node(object):
def __init__(self,elem):
self.elem = elem
self.next = None
单向循环链表的构建、判断链表是否为空
单向循环链表的构建与单向链表略有差异,如果在建立链表时赋了一个值,要将该节点的后继区域与头结点连接。
判断是否为空的操作与单向链表相同。
class SingleCycleLinkList(object):
'''单向循环链表'''
def __init__(self,node = None):
self.__head = node
if node: # node存在实际值,不是None
node.next = node
def is_empty(self):
return self.__head == None
计算链表长度、遍历链表
实现计算链表长度和遍历链表时,与单向链表有一个共同的区别点,就是循环体中不能对最后一个节点进行操作,所以要补充尾节点的情况:对于计算长度是count初始为1 ,对于遍历链表是最后再print一次。
def length(self):
'''计算链表长度'''
if self.is_empty(): # 空列表的情况
return 0
else:
cur = self.__head
count = 1 # 注意count初始要置1
while cur.next != self.__head: # 不能使用cur != self.__head的判断语句
count += 1
cur = cur.next
return count
def travel(self):
'''遍历链表'''
if not self.is_empty(): # 不能是空链表
cur = self.__head
while cur.next != self.__head:
print(cur.elem,end = ' ')
cur = cur.next
# 把尾节点的值补上
print(cur.elem)
print("")
else: # 空链表返回空
return
头部添加元素
def add(self, item):
'''链表头部添加元素:头插法'''
node = Node(item)
cur = self.__head
if self.is_empty(): # ※考虑空列表的情况
self.__head = node
node.next = self.__head
else:
# ※※※一定要先循环,使游标指向最后!否则如果先进行把指定节点放到第一位的操作,会使self.__head值有变化,导致进入死循环!
while cur.next != self.__head:
cur = cur.next
node.next = self.__head
self.__head = node
# 退出循环后,cur指向的就是尾节点
cur.next = node
尾部添加元素
def append(self, item):
'''链表尾部添加元素:尾插法'''
node = Node(item)
cur = self.__head
if self.is_empty():
self.__head = node
node.next = self.__head
else:
while cur.next != self.__head:
cur = cur.next
cur.next = node
node.next = self.__head
指定部位添加元素
def insert(self,pos,item):
'''链表指定部分添加元素'''
node = Node(item)
count = 0
pre = self.__head
if pos <= 0:
self.add(item)
elif pos >= self.length()-1:
self.append(item)
else:
while count != pos-1:
pre = pre.next
count += 1
node.next = pre.next
pre.next = node
删除节点
def remove(self,item):
'''删除链表中第一个出现的指定元素'''
cur = self.__head
pre = None #采用两个游标
if self.is_empty():
return
else:
while cur.next != self.__head: # ※注意!①尾节点无法进入循环!②空链表会报错
if cur.elem == item:
# 头部节点的删除
if pre == None:
rear = self.__head
while rear.next != self.__head: # 找到尾节点 ※要先找节点,因为后边改变了self.__head的值!
rear = rear.next
self.__head = cur.next
rear.next = self.__head
# 中间节点的删除
else:
pre.next = cur.next
return # 在单项链表中,使用break就相当于跳出了整个函数,但是在这里,break跳出循环后还会执行后边的语句,所以要用return来跳出整个函数
else:
pre = cur
cur = cur.next
# 退出循环时,游标指向尾节点
# 尾部节点的删除
if cur.elem == item:
if self.__head == cur: # ※考虑只有一个节点的情况,否则pre.next会报错
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
return False