最近在跟着卡尔老师学算法,所以现在不定时会更新自己所学的内容,毕竟最好的学就是教嘛:)
什么是链表?
链表(Linked List)是一种基础的数据结构,用于组织和存储一系列元素。链表中的元素被组织成节点,每个节点包含数据和一个指向下一个节点的引用(指针或链接),即每个节点包含了两种内容:数据域和指针域。
链表的类型
1.单链表
在单向列表中,节点有数据域data与指针域next,链表的最后一个节点的next指向空。
2.双链表
上面说到单链表只有一个指针域,因此只能向后查找,而双链表具有两个指针域,就可以兼顾向后查找与向前查找了。
3.循环链表
循环链表,顾名思义就是链表的首尾相连,是一个闭环结构。
链表基础功能的实现
1.初始化节点与单向链表
以单链表为例,单链表具有一个数据域和一个指针域用于指向下一个节点,因此可以在python中利用class结构定义节点,如下:
#初始化节点
class Node:
def __init__(self,data,next):
self.data=data #数据域
self.next=next #指针域,通常赋值为null或者另一个Node节点,表示指针指向下一个节点
#初始化单向链表
class LinkedList:
def __init__(self):
self.head=None #头节点
在Python中,__init__
方法是一个特殊的方法,用于在创建类的实例时进行初始化。第一个参数通常被命名为 self
,表示类的实例本身。在__init__
方法内部,可以为该实例设置属性,并不需要在输入参数中定义。
2.在尾部添加节点
思路为:
1.先新建一个节点new_Node,data为输入值,next为null;
2.检测单向链表中有没有节点,如果没有,直接将头节点指向该新建节点即可。如果有,则进入下一步;
3.可以额外建立一个新节点last_Node,用该节点进行遍历,一直到链表末端。遍历过程为将头节点赋值给last_Node,利用while循环不断判断last_Node的next是否为空,当为空时,last_Node就到了链表的最后一个节点位置,此时只需要将next_Node的next指向new_Node即可。
如下图所示:
代码如下:
#添加节点
def append(self,data):
new_node = Node(data,next=None) #新建节点,数据域为传入参数data,next赋为None
#如果当前链表为空,那么链表的头节点为新建节点
if self.head == None:
self.head = new_node
return True
#如果不为空,就从链表的头节点开始遍历,直到找到next为None的尾节点
last_Node = self.head #新建一节点,赋值为链表头节点
while last_Node.next != None: #当前节点的指针域不指向None时,说明当前节点不为尾节点
last_Node = last_Node.next
#循环结束,last_Node为尾节点
last_Node.next = new_node
好了,现在我们完成了一个添加节点的功能,那么我们该怎么测试一下写的对不对呢?
就再写一下第二个基础功能,打印链表吧。
3.打印链表
打印链表的思路其实就是遍历输出,与上一个基础功能中的步骤三很像,只不过就是在遍历的过程中多加了一个print函数,代码如下:
#输出链表
def show(self):
current_node = self.head #新设置一个节点,赋值为头节点
while current_node : #只要当前节点不为空,就一直输出
print(current_node.data,end=" ")
current_node = current_node.next
print() #这里的print单纯换行作用
这里还要注意一点,在添加节点功能的遍历过程中,while的条件是current_node的next不为空,这样循环在终止时,current_node是刚好到了链表中的最后一个元素就退出循环了,如果在打印过程中还这么写就会略去最后一个元素。因此这里的终止条件采用判断当前节点是否为空。
接下来我们就可以先测试一下我们的代码了,到目前为止的整体代码如下:
#定义节点
class Node:
def __init__(self,data,next):
self.data = data
self.next = next
#定义单向链表
class LinkedList:
def __init__(self):
self.head = None
def append(self,data):
new_Node = Node(data,next=None)
if self.head == None :
self.head = new_Node
return True
last_Node = self.head
while(last_Node.next):
last_Node = last_Node.next
last_Node.next = new_Node
return True
def show(self):
current_node = self.head #新设置一个节点,赋值为头节点
while current_node : #只要当前节点不为空,就一直输出
print(current_node.data,end=" ")
current_node = current_node.next
print() #这里的print单纯换行作用
#新建一个链表
list = LinkedList()
list.append(1)
list.append(2)
list.append(4)
list.append(8)
list.append(16)
list.append(32)
list.show()
结果如下:
1 2 4 8 16 32
现在已经可以在链表尾部添加节点并打印展示了。
4.在首部添加节点
当然,除了在尾部添加节点外,链表添加节点的方式还有从首部添加以及从链表中间位置添加,下面就先来介绍较为简单的首部添加节点。
思路为,新建一个节点new_Node,将其next指向头节点。只是这样就可以了吗?当然不对,还要将代表该链表起始位置的头节点换成新节点才可以,这样新添加的节点才能作为新链表的头节点,否则打印还是从原本的头节点开始打印的。如下图所示:
明显,代码应写为如下:
def append_head(self,data):
new_Node = Node(data,next=None) #新设插入节点
new_Node.next = self.head #将新节点的next赋值为头节点
self.head = new_Node #头节点更新为新节点
5.在链表中插入节点
在链表中插入节点,需要将原链表中的两个节点的连接打破,然后两个节点分别作为新节点的头与尾,如下所示:
因此,实现的时候要同时记录下两个节点的位置,设当前节点为current_Node,前一节点为pre_Node,那么只需要再设置一个变量用于计算位置,就可以准确找到需要插入的点了。找到之后,将pre_Node的next指向new_Node,将new_Node的next指向current_Node即可。代码实现如下:
#在第index个节点前插入新节点
def append_mid(self,data,index):
new_Node = Node(data,next=None) #需要插入的节点
current_Node = self.head #遍历节点,从头节点开始
pre_Node = None #前一节点,先设为None
cnt = 1 #计数,用于测量位置
if index <= 0:
print("index应大于等于1")
return False
#特殊情况,当需要在首部插入元素时
if index == 1:
self.append_head(data)
return True
#开始遍历,找到需要插入节点的位置
while cnt < index and current_Node:
pre_Node = current_Node #将前一节点赋值为current_Node
current_Node = current_Node.next #将当前节点赋为下一节点
cnt += 1 #位置加一
pre_Node.next = new_Node
new_Node.next = current_Node
return True
测试代码:
#定义节点
class Node:
def __init__(self,data,next):
self.data = data
self.next = next
#定义单向链表
class LinkedList:
def __init__(self):
self.head = None
def append(self,data):
new_Node = Node(data,next=None)
if self.head == None :
self.head = new_Node
return True
last_Node = self.head
while(last_Node.next):
last_Node = last_Node.next
last_Node.next = new_Node
return True
def append_head(self,data):
new_Node = Node(data,next=None)
new_Node.next = self.head
self.head = new_Node
def append_mid(self,data,index):
new_Node = Node(data,next=None)
current_node = self.head
pre_Node = None
cnt = 1
if index == 1:
self.append_head(data)
return False
while cnt < index and current_node:
pre_Node = current_node
current_node = current_node.next
cnt += 1
pre_Node.next = new_Node
new_Node.next = current_node
def show(self):
current_node = self.head #新设置一个节点,赋值为头节点
while current_node : #只要当前节点不为空,就一直输出
print(current_node.data,end=" ")
current_node = current_node.next
print() #这里的print单纯换行作用
#新建一个链表
list = LinkedList()
list.append(1)
list.append(2)
list.append(4)
list.append(8)
list.append(16)
list.append(32)
list.show()
list.append_head(0)
list.show()
list.append_mid(5,3)
list.show()
测试结果:
1 2 4 8 16 32
0 1 2 4 8 16 32
0 1 5 2 4 8 16 32
到目前为止,我们已经成功实现了单向链表的添加节点操作与打印操作,下面再来学习一下如何删除链表中的节点。
5.删除链表中的某个节点
插入节点是将前一节点与后一节点的连接打碎,然后将前一节点的next指向新建的节点,将新建节点的next指向后一节点的过程。那么我们反过来想,删除节点是怎么操作呢?也是将三个节点之间的连接打碎,然后将前一节点的next跳过中间节点,直接指向下一节点即可,如下所示:
那么我们很容易想到,在代码实现中,我们同样需要用一个计数器来测量位置,用一个current_Node节点来遍历链表,以此找到需要删除的节点位置,同时还需要一个pre_Node节点来记录前一节点的位置。这样找到需要删除的节点后,只需要将pre_Node的next指向当前节点current_Node的next节点即可。
代码实现如下:
#删除某位的节点,index从1开始,表示第几个位置的节点
def delete_Node(self,index):
if index < 1:
print("无效输入")
return False
current_Node = self.head #作为遍历节点
pre_Node = None
cnt = 1 #计数器,计量位置
if index == 1:
self.head = self.head.next
return True
while cnt < index :
cnt += 1
if current_Node.next:
pre_Node = current_Node
current_Node = current_Node.next
else:
return False #这里代表index已经超过了链表的长度,需要删除的节点不存在
pre_Node.next = current_Node.next
return True
至此,python中链表的基本操作差不多就结束了,下面就要开始学习力扣上链表的相关题目了,欢迎大家批评指正!