文章目录
一、链表的概念
1、为什么需要链表
- 顺序表的构建需要预先指导数据大小来申请连续的存储空间,在进行扩充时还需要进行数据的搬迁,所以使用起来不是很方便
- 链表结构可以充分利用计算机的内存空间,实现灵活的内存动态管理
- 链表和顺序表都是线性表
2、链表的定义
链表是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是在每个数据存储单元作为一个节点,并在节点里存放下一个节点的位置信息(即地址),像一条链条一样将每个数据存储单元连接起来,有顺序的存储数据。
数据存储单元(节点):
数据区 | 链接区 |
---|---|
存放数据 | 存放下一个数据的地址 |
二、单向链表
1、单链表的定义
单向链表也叫单链表,是链表中最简单的一种形式,指链表只是单向的。它的每个节点包含两个域,一个信息域(存放数据/元素)一个链接域(存放下一个元素的地址),这个谅解指向链表中的下一个节点,最后一个节点的链接域指向一个空值(用一竖一横标识)。
单链表节点:
表元素域 | 下一节点链接域 |
---|---|
元素elem | 下一节点地址next |
单链表:
- 表元素域用来存放具体的数据
- 链接域用来存放下一个节点的位置
- 变量P指向链表的头节点(首节点)的位置,从P出发能找到表中的任意节点
2、python中的变量赋值
比如:
a=10,b=20,如何得到a=20,b=10的结果?
python中:a,b=b,a即可实现
解答:
- 在python中变量的赋值不是将数据存放在变量中,而是数据本身有自己的内存,当某个数据被赋值给变量时,变量就指向数据所在的位置,此时数据的地址还是原来的地址,但是变量的指向改变了,如下图
- 当发生a,b=b,a时,在等号右边,此时b=20,a=10,因此会有a,b=20,10,然后等号左边的变量发生改变,也就是变量的指向发生改变,数据位置不改变,如下图
对于单链表中的节点:
elem=10表示:elem变量指向了存储数据10的地址
next=node2表示:next变量指向node2存储的地址
所以在python中没有特殊的地址变量,而是用等号来连接变量和变量表示的数据,变量等于谁,就将自己指向数据存放的地方
3、单链表在python中的实现
1)节点的操作
# 首先实现节点,elem表示数据、next表示下一节点的链接
class Node(object):
def __init__(self,elem):
self.elem=elem
self.next=None
2)单链表的操作
操作 | 描述 |
---|---|
is_empty() | 链表是否为空 |
length() | 链表长度 |
travel() | 遍历整个链表 |
add(item) | 链表头部添加元素 |
append(item) | 链表尾部添加元素 |
insert(pos,item) | 链表指定位置添加元素 |
remove(item) | 删除节点 |
search(item) | 查找节点是否存在 |
# 实现单链表
# 因为单链表的增加元素、查看元素等操作是类实例化后的某个对象的操作,因此使用def定义
class SingleLinkList(object):
# 定义构造函数,为了初始化对象时将必须用到的属性、方法做一些定义
def __init__(self,node=None):
# node=None时表示头节点为空,即P指向的是一个空链表
# 也可以先创建一个node,然后将创建好的node传进来
# 又由于self.head属性是只限于类中使用的属性,因此是私有
# 通过双下划线定义私有属性
self.__head=node
def is_empty(self):
"""判断列表是否为空"""
return self.__head==None
def length(self):
"""获取链表长度"""
# 设置一个辅助工具cur,用来移动遍历节点
# cur刚开始指向head,最后指向None
cur=self.__head
# count用来记录数量
count=0
# 刚开始为0 表示刚开始指向head,与最后指向None对应
while(cur != None):
count += 1
cur=cur.next # 游标指向游标的next指向的位置
return count
def travel(self):
"""遍历整个链表"""
cur=self.__head
while(cur != None):
# 通过end=' '空一格
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插入元素
:param pos 从0开始
"""
if pos <= 0:
self.add(item)
elif pos > (self.length()-1):
self.append(item)
else:
node=Node(item)
cur=self.__head
count=0
while count < (pos-1):
count += 1
cur=cur.next
node.next=cur.next
cur.next=node
def remove(self,item):
"""删除节点,删除遍历找到的第一个符合的值"""
# cur表示当前节点,pre表示前一个节点
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
结果
此时链表是否为空 True
此时链表长度为: 0
此时链表是否为空 False
此时链表长度为: 1
8 1 2 3 4
9 8 1 2 3 4
100 9 8 1 2 3 4
100 9 8 1 2 3 4 300
9 8 1 2 3 4 300
三、双向链表
1、双向链表的定义
- “双向链表”或“双面链表”。
- 每个节点有两个链接:
- 一个指向前一个节点,当此节点为第一个节点时,指向空值;
- 而另一个指向下一个节点,当此节点为最后一个节点时,指向空值。
2、双向链表的操作
- is_empty() 链表是否为空
- length() 链表长度
- travel() 遍历链表
- add(item) 链表头部添加
- append(item) 链表尾部添加
- insert(pos,item) 指定位置插入元素
- remove(item) 删除节点
- search(item) 查找节点是否存在
3、python中实现双向链表操作
# 定义节点
class Node(object):
"""节点,有前端节点,后端节点"""
def __init__(self,item):
self.elem=item
self.next=None
self.prev=None
# 定义双向链表的操作
class DoubleLinkList(object):
def __init__(self,node=None):
self.__head=node
def is_empty(self):
# 判断时也可以用is,is判断(id,type,value)是否相同,=判断(type,value)是否相同
return self.__head == None
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)
# self.__head 表示头部指向的节点
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):
# 再指定位置添加元素,在创建连接时,要看之前是否已经改变连接,根据实时的连接来定义新的连接
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.pre
cur.pre.next=node
cur.pre=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
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__':
dobllink=DoubleLinkList()
print('此时链表是否为空', dobllink.is_empty())
print('此时链表长度为:', dobllink.length())
dobllink.append(1)
print('此时链表是否为空', dobllink.is_empty())
print('此时链表长度为:', dobllink.length())
dobllink.append(2)
dobllink.add(8)
dobllink.append(3)
dobllink.append(4)
dobllink.travel()
dobllink.insert(-1, 9)
dobllink.travel()
dobllink.insert(-1, 100)
dobllink.travel()
dobllink.insert(10, 100)
dobllink.travel()
dobllink.remove(100)
dobllink.travel()
结果:
此时链表是否为空 True
此时链表长度为: 0
此时链表是否为空 False
此时链表长度为: 1
8 1 2 3 4
9 8 1 2 3 4
100 9 8 1 2 3 4
100 9 8 1 2 3 4 100
9 8 1 2 3 4 100