双向链表是一种线性数据结构,其中每个节点包含两个指针:一个指向前驱节点(prev),另一个指向后继节点(next)。相比单向链表,双向链表支持双向遍历,使得插入和删除操作更加高效,尤其是在已知节点位置的情况下。
双向链表操作详解
1. 插入节点
在双向链表中插入新节点时,需调整前后节点的指针以维护链表结构。假设要在某个节点 p 之后插入新节点 new_node:
def insert_after(head, p, new_node):
new_node.next = p.next
new_node.prev = p
if p.next is not None:
p.next.prev = new_node
p.next = new_node
图 3-4(a) 展示了插入过程中各指针的变化过程:
p → new_node → p.next,同时反向指针也正确连接。
2. 删除节点
删除节点时,需将其前驱和后继节点相互链接,再释放该节点内存。
def delete_node(head, node):
if node.prev is not None:
node.prev.next = node.next
else:
head = node.next # 删除的是头节点
if node.next is not None:
node.next.prev = node.prev
node.prev = node.next = None # 清空被删节点指针
图 3-4(b) 显示了删除后相邻节点如何绕过目标节点完成连接。
栈的基础知识
栈(Stack)是限定仅在表尾(栈顶)进行插入或删除操作的线性表,遵循“后进先出”(LIFO, Last In First Out)原则。
基本操作:
- 初始化:创建一个空栈。
- 判空(isEmpty):判断栈是否为空。
- 入栈(push):将元素压入栈顶。
- 出栈(pop):移除并返回栈顶元素。
- 取栈顶元素(peek/top):不删除地访问栈顶元素。
使用场景:
- 函数调用堆栈
- 表达式求值与括号匹配
- 深度优先搜索(DFS)
栈可以用数组或链表实现;若用双向链表实现,则可更灵活地管理内存,但通常使用单向链表或顺序存储即可满足需求。
补充背景说明
《软件设计师教程》作为软考指定教材,系统讲解了上述数据结构内容,强调理论与实践结合,帮助考生掌握程序设计基础、算法分析及系统开发能力。双向链表与栈均属于数据结构章节的重点内容,常出现在选择题与综合应用题中。
使用双向链表实现栈时,可以将链表的头节点作为栈顶(也可以选择尾节点,但头部操作更高效),所有入栈和出栈操作均在头部进行。由于双向链表支持快速访问前驱和后继,插入和删除的时间复杂度均为 O(1)。
实现思路:
- 栈顶:设为双向链表的头节点位置。
- 入栈(push):在链表头部插入新节点。
- 出栈(pop):删除头节点并返回其值。
- 取栈顶(peek):返回头节点的值而不删除。
定义节点结构
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
栈的实现(基于双向链表)
class Stack:
def __init__(self):
self.head = None # 指向栈顶(链表头)
def push(self, data):
new_node = Node(data)
if self.head is None:
self.head = new_node
else:
new_node.next = self.head
self.head.prev = new_node
self.head = new_node
def pop(self):
if self.head is None:
raise IndexError("pop from empty stack")
popped_data = self.head.data
self.head = self.head.next
if self.head is not None:
self.head.prev = None
return popped_data
def peek(self):
if self.head is None:
return None
return self.head.data
def is_empty(self):
return self.head is None
示例使用
stack = Stack()
stack.push(10)
stack.push(20)
print(stack.peek()) # 输出: 20
print(stack.pop()) # 输出: 20
print(stack.pop()) # 输出: 10
print(stack.is_empty()) # 输出: True
时间复杂度分析
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
push | O(1) | 在头部插入,只需修改几个指针 |
pop | O(1) | 删除头节点,指针调整常数时间完成 |
peek | O(1) | 直接访问头节点数据 |
is_empty | O(1) | 判断 head 是否为 None |
所有核心操作都具有 O(1) 的时间复杂度,效率非常高。
优势与适用场景
- 动态大小,避免数组扩容问题。
- 内存利用率高,按需分配。
- 适合频繁插入/删除的场景。



被折叠的 条评论
为什么被折叠?



