链表
链表是有0个或多个相互间有连接的结点构成的。每个结点包含数据区和链接取(指针区)
1. 单链表
1.1 单链表的特性
- 这是通过单链表构成的一个数组Li = [200,400,600]]
- 每个结点的指针区指向下一个节点
- 最后一个节点指向None
- 链表有一个头指针
1.2 单链表支持的操作
链表由结点构成,在建链表前,新建结点类
结点类
class Node(object):
def __init__(self,item):
self.element = item
self.next = None
链表类
class Single_link_list(object):
def __init__(self,node=None):
self.__head = node
- 是否为空链表 链表的头结点是否为空
def is_empty(self):
return self.__head == None
- 链表长度 遍历链表计算长度
def length(self):
cursor = self.__head # 记录当前指针所在位置
count = 0 # 记录元素个数
while cursor != None:# cursor不为空,一直计数,指针向后移动
count += 1
cursor = cursor.next
return count
- 链表头部添加元素
在现有链表[20,40]的头部插入一个值为60的结点
先让node的指针指向head指向的区域,然后让head指向node
代码
def insert_head(self, item): # 在头部插入
node = Node(item)
node.next = self.__head
self.__head = node
- 链表尾部加入元素
如果是空链表,直接让head指针指向新建的结点。否则,找到链表的尾部结点,让尾部结点指向新建的结点
def append(self, item):
'''
在尾部插入
:param item: 待插入的数
:return: None
'''
node = Node(item)
# 若链表为空,cursor无next属性
if self.is_empty():
self.__head = node
return
# 链表不为空,遍历到尾部,尾部指针指向新建的节点
cursor = self.__head
while cursor.next != None: # 找尾部结点
cursor = cursor.next
cursor.next = node
- 链表任意位置插入元素 insert
在头部和尾部插入参考上面的
在现有链表[60,20,40]的中间插入一个值为80的结点,新链表为[60,20,80,40]。先找到插入的位置,及cursor
指向的位置,然后node.next指向cursor,在让cursor的前一个结点指向node.
这里有两个问题:
- 怎么找到插入的位置?
- cursor的前一个结点怎么表示
关于插入的位置pos?
定义记录位置的变量count
及初始指向链表部的指针cursor=self.__head
。count累加,cursor向后移动,当cout==pos
时,cursor
也移动到了待插入的位置
cursor的前一个结点表示
定义指针pre=None
,cursor
在移向下一个位置前,将cursor的值赋给pre,这样,pre和cursor间始终相隔一个结点。
代码实现
def insert(self, pos, item):
'''
:param pos: 插入位置
:param item: 插入元素
:return: None
'''
if pos < 1: # 位置<1,从头部插入
self.insert_head(item)
return
if pos >= self.length():# 从尾部插入
self.append(item)
return
node = Node(item)
count = 0 # 记录当前指针指向的位置
pre = None # 初始化前驱结点
cursor = self.__head # 初始化当前结点
while count < pos:
# 还没找到时,指针一直向后移动
pre = cursor
cursor = cursor.next
count += 1
# 找到位置后,改变node指针指向
node.next = cursor
pre.next = node
- 删除元素
头结点删除
现有链表[60,20,80,40],删除60这个元素,怎么操作?
- 直接让头结点指向当前结点的下一个结点
非头结点删除
现有链表[60,20,80,40],删除80这个元素,怎么操作? - 找到80这个结点,cursor指向80这个结点,然后让结点20的指针指向结点80的后面一个结点40。
- 没找到时,cursor和pre一直向后移动
- 向后移动的终止条件是,找到了待删除的元素或者cursor移动到了链表外
def remove(self,item):
'''
删除元素
:param item:
:return:
'''
if self.is_empty():
#空链表
return
# 需要有前后指针,pre.next = cursor
pre = None
cursor = self.__head
while cursor != None:
if cursor.element == item:
if pre: # 删除的不是头结点
pre.next = cursor.next
else:# 删掉的是头结点
self.__head = cursor.next
return
else:
pre = cursor
cursor = cursor.next
- 查找链表
def search(self, item):
if self.is_empty():
return False
cursor = self.__head
while cursor != None:
if cursor.element == item:
return True
else:
cursor = cursor.next
return False
- 遍历链表
def travel(self):
'''
遍历链表,输出
:return: None
'''
# if self.is_empty():
# return
cursor = self.__head
while cursor != None:
print(cursor.element, end=' ')
cursor = cursor.next
print()
2. 双向链表
2.1 双向链表的特性
- 这是通过单链表构成的一个数组Li = [20,40,60]]
- 每个结点有两个指针:前驱指针和后继指针,前驱指针指向前一个结点,后继指针指向下一个节点
- 第一个结点的前驱指针指向None,最后一个节点后继指针指向None
- 链表有一个头指针
- 与单向链表相比,双向链表多了一个前驱指针
2.2 单链表支持的操作
与单向链表相比,双向链表多了一个前驱指针。在基本操作实现上,部分双向链表的操作与单向链表操作相同
- 判断是否为空链表,只需判断头结点是否为空即可,这与单链表代码实现相同
- 计算链表长度、查找链表、遍历链表都只需后继指针向后移动即可,不涉及到前驱指针的操作,所以,这些也与单链表代码实现相同
- 定义结点类
class Node(object):
'''
双向链表,每个节点有一个前驱指针,一个后继指针,一个元素区
'''
def __init__(self,item):
self.element = item
self.next = None
self.pre = None
- 链表头部添加元素
- 空链表头部添加
与单向链表相同 - 非空链表头部添加
def insert_head(self, item): # 在头部插入
node = Node(item)
if self.is_empty():
self.__head = node
else:
node.next = self.__head
self.__head.pre = node
self.__head = node
- 在尾部添加
- 链表为空,与单向链表相同,直接将头指针指向新建结点
- 非空链表尾部添加。原始链表[20,40,60],在尾部添加后,变为[20,40,60,80]
def append(self, item):
'''
在尾部插入
:param item: 待插入的数
:return: None
'''
node = Node(item)
# 若链表为空,cursor无next属性
if self.is_empty():
self.__head = node
return
# 链表不为空,遍历到尾部,尾部指针指向新建的节点
cursor = self.__head
while cursor.next != None:
cursor = cursor.next
cursor.next = node # 指向后面
node.pre = cursor # 指向前面
- 在任意位置添加
- 在头部和尾部插入参考
insert_head和append
- 在中间部分插入,还需要考虑前驱指针变化
插入时,先保留原有的连接,将新建结点链接到该插入的位置,然后改变原有链接
1). 新建结点的前驱指向当前结点的前一个结点node.pre = cursor.pre
2). 新建结点的后继指向当前结点node.next = cursor
3). 当前结点的前一个结点的后继指向新建结点cursor.pre.next = node
4). 当前结点的前驱指向新建结点cursor.pre= node
代码实现
def insert(self, pos, item):
'''
:param pos: 插入位置
:param item: 插入元素
:return: None
'''
if pos < 1: # 位置<1,从头部插入
self.insert_head(item)
return
if pos >= self.length():
self.append(item)
return
node = Node(item)
count = 0
# pre = None# 不需要pre
cursor = self.__head
while count < pos:
count += 1
cursor = cursor.next
node.next = cursor
node.pre = cursor.pre
cursor.pre.next = node
cursor.pre = node
- 删除链表元素
- 删除的是头结点,
self.__head = cursor.next, self.__head.pre = None
- 一般情况删除的示意图
找出待删除的元素,需要遍历链表。遍历结束的条件是,找到元素或者找到了最后一个元素。如果是找到了元素,按图示操作即可,如果是跳出了循环,还有最后一个元素没有比较,需在循环外比较。
def remove(self,item):
'''
删除元素
:param item:
:return:
'''
if self.is_empty():
#空链表
return
# 需要有前后指针,pre.next = cursor
cursor = self.__head
while cursor.next != None:
if cursor.element == item:
if cursor.pre:# 删除的不是头结点
cursor.pre.next = cursor.next
cursor.next.pre = cursor.pre
else:#删除的是头结点
self.__head = cursor.next
self.__head.pre = None
return
else:
cursor = cursor.next
if cursor.element == item: # 待删除元素与最后一个元素比较
cursor.pre.next = None
3. 单向循环链表
3.1 单向循环链表的特性
与单向链表相比,每个结点也是包含数据区和指针区。不同的是尾结点的指针指向的是头结点
3.2 单向循环链表的基本操作
由于单向循环链表的最后一个结点的指针指向第一个结点,所以遍历链表的循环条件是cursor.next != self.__head
。
- 结点定义与单向链表相同
class Node(object):
def __init__(self,item):
self.element = item
self.next = None
- 单向链表初始化
用非空结点初始化时,让结点的next指针指向自身
class Single_cycle_link_list(object):
def __init__(self,node=None):
self.__head = node
if node:
node.next = node
- 是否为空的判断与单向列链表相同
- 求链表长度
def length(self):
cursor = self.__head
if self.is_empty():
return 0
count = 1
while cursor.next != self.__head: # cursor不指向队首,一直计数,指针向后移动
count += 1
cursor = cursor.next
return count
- 在头部插入元素
def insert_head(self, item): # 在头部插入
node = Node(item)
if self.is_empty():
self.__head = node
if node:
node.next = node
else:
node.next = self.__head
self.__head.pre = node
self.__head = node
- 在尾部插入
def append(self, item):
'''
在尾部插入
:param item: 待插入的数
:return: None
'''
node = Node(item)
# 若链表为空,cursor无next属性
if self.is_empty():
self.__head = node
return
# 链表不为空,遍历到尾部,尾部指针指向新建的节点
cursor = self.__head
while cursor.next != None:
cursor = cursor.next
cursor.next = node # 指向后面
node.pre = cursor # 指向前面
- 在任意位置插入————与单向链表相同
- 删除元素
- 链表是否为空
- 删除的是否是头结点
- 链表最后一个元素的处理
def remove(self,item):
'''
删除元素
:param item:
:return:
'''
if self.is_empty():
#空链表
return
# 需要有前后指针,pre.next = cursor
cursor = self.__head
while cursor.next != None:
if cursor.element == item:
if cursor.pre:# 删除的不是头结点
cursor.pre.next = cursor.next
cursor.next.pre = cursor.pre
else:#删除的是头结点
self.__head = cursor.next
self.__head.pre = None
return
else:
cursor = cursor.next
if cursor.element == item:
cursor.pre.next = None
附:
python中的变量表示的是数据的地址,当交换变量值是,实际是交换变量指向的区域