单链表的定义与案例(Python实现)
1. 结点与链表
链表中的基本单元是结点,上面绘制了一个基本结点的基本架构,可以看出每个结点包含两个部分:
- val部分,是指结点的数据区域,用于存储结点本身的数据信息;它不局限于一个数据,可以是多个数据。
- next部分,是指结点的指针域,用来存储其直接后继的结点信息;next的值指向下个结点的地址,如果当前结点为最后一个结点,next的值设置为None;
class Node:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
Python中,结点的基本结构如图1和Node所示;
链表是指线性表的链式存储结构;同普通的数组相比,具有以下几个优势:
- 存储单元不一定连续,是用一组任意的存储单元来存储链表数据的;
- 链表的长度是动态变化的,可以方便的实现节点的插入和删除;
- 链表的每个元素都是一个节点,每个节点除存储节点数据外,还存储其直接后继关系;
头结点、头指针、首元结点
- 头指针(必须有):永远指向链表从第一个结点的位置(如果链表有头结点,头指针指向头结点;否则,头指针指向首元结点)
- 头结点(可没有):不是单链表中必须的,但是有时候在链表的第一个结点之前额外增设一个结点(数据域一般不放数据);
- 首元结点:链表中第一个元素所在的结点,是指头结点后的第一个结点。
注意:
- 若头结点的指针域为空,则表示该链表为空;
- 头结点对于单链表并不是必须的;
- 单链表中可以没有头结点,但不能没有头指针;
头指针与头结点的区别
- 头指针是一个指针,指向链表的头结点或者首元结点;
- 头结点是一个实际存在的结点,包含指针域和数据域;
- 头指针只声明而没有分配存储空间,但头结点进行了声明并分类了一个结点的实际无聊内存
2. 链表的构建、遍历等
2.1 新建链表, 只有简单的从头和尾部插入示范
class LinkedList(object):
""" 构建单链表结构"""
def __init__(self):
self.head = None #定义头指针,并使得头指针指向链表的第一个结点
self.length = 0
def is_empty(self):
# 判断链表是否为空(只需要判断头指针指向的头结点是否为空)
return self.head == None
def add_node_from_head(self, new_node):
# 从头部插入结点
if self.is_empty():
self.head = new_node
else:
new_node.next = self.head
self.head = new_node
self.length += 1
def add_node_from_end(self, new_node):
# 从尾部插入结点,需要先遍历完所有结点,然后再找到最后一个结点
if self.is_empty():
self.head = new_node
else:
last_node = self.head
while last_node.next is not None:
last_node = last_node.next
last_node.next = new_node
self.length += 1
def travel(self):
#遍历链表,从头指针开始,依次往下读取
p = self.head
while p:
print(p.val, end=' ')
p = p.next
接下来,我们将0-4按顺序以end方式插入链表,并将5-9按照head方式插入链表,然后输出链表:
for t in range(10):
new_node = Node(t)
if t <5:
linklist.add_node_from_end(new_node)
else:
linklist.add_node_from_head(new_node)
linklist.travel()
输出结果如下:
9 8 7 6 5 0 1 2 3 4
2.2 复杂的链表插入与删除
接下来,我们对链表执行一些更为复杂的操作,比如删除结点,给定插入位置等
#为了减少代码的重写率,在这里我们新建一个链表,通过继承上一个链表的属性来使用前面定义的一些方法
class LinkedList2(LinkedList):
def __init__(self):
super(LinkedList2, self).__init__()
def add_node_from_index(self, node, index):
if (index > (self.length+1)) or index <= 0:
while(index > self.length + 1 or index <= 0):
print("插入的位置超出边界,请输入[{}-{}]的范围".format(1, self.length+1))
index = eval(input())
if index == 1:
self.add_node_from_head(node)
elif index == 2:
node.next = self.head.next
self.head.next = node
self.length += 1
else:
# 先遍历出当前index的前一个node;然后进行插入操作
current_node = self.head
for i in range(1, index-1):
current_node = current_node.next
node.next = current_node.next
current_node.next = node
self.length += 1
def delete_node_from_index(self, index):
# 按照索引删除结点
if (index > self.length) or index <= 0:
while(index > self.length or index <= 0):
print("插入的位置超出边界,请输入[{}-{}]的范围".format(1, self.length+1))
index = eval(input())
if index == 1:
self.head = self.head.next
current_node = self.head
elif index == 2:
current_node = self.head
current_node.next = current_node.next.next
else:
current_node = self.head
for i in range(1, index-1):
current_node = current_node.next
current_node.next = current_node.next.next
self.length -= 1
接下来,我们将0-4按顺序以end方式插入链表,并将5-9按照head方式插入链表,然后输出链表
9 8 7 6 5 0 1 2 3 4
然后我们在5的位置插入结点11, 删除6位置的结点, 输出结果如下:
ll2.add_node_from_index(Node(11), 5)
ll2.travel()
ll2.delete_node_from_index(6)
ll2.travel()
9 8 7 6 11 5 0 1 2 3 4
9 8 7 6 11 0 1 2 3 4
3. 单链表处理题目示例
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
解题思路
- 首先分析题目可知,需要生成一个新链表,那么我们先定义一个新链表的头结点 head_node
- 然后定义可移动指针prev,使其指向头结点head_node
- 当l1或l2为空时,直接将其加入到新链表中
- 当l1和l2均不为空时,判断l1和l2中头结点值的大小,并将prev指向较小的结点, 并将l1指向l1.next或l2指向l2.next;
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
head_node = ListNode(-1) # 构建头结点, 辅助链表构建
prev = head_node #指针指向头结点
while list1 and list2:
if list1.val <= list2.val:
prev.next = list1
list1 = list1.next
else:
prev.next = list2
list2 = list2.next
if list1 is not None: # l1不为空,直接加入到链表中
prev.next = list1
else:
prev.next = list2
return head_node.next #返回除了头结点外的链表
好啦,关于单链表的相关操作和信息就分享到这啦。
如果内容有误,欢迎大家批评指正哦~~