
本文详解链表区间反转算法的核心逻辑,重点剖析 reverse_between(start_index, end_index) 方法中“前置节点定位”机制的原理与必要性,并通过可视化步骤和代码实践说明为何 previous_node 必须停在 start_index - 1 位置。
本文详解链表区间反转算法的核心逻辑,重点剖析 `reverse_between(start_index, end_index)` 方法中“前置节点定位”机制的原理与必要性,并通过可视化步骤和代码实践说明为何 `previous_node` 必须停在 `start_index - 1` 位置。
在链表操作中,原地反转指定区间(如从索引 start_index 到 end_index) 是一个经典且高频的面试题。其难点不在于反转本身,而在于如何安全、精准地“接入”和“断开”子链——这正是 previous_node 定位逻辑的关键所在。
为什么需要 dummy 节点与前置定位?
直接操作头节点会引入边界特判(如 start_index == 0)。为统一处理,算法引入 dummy_node 作为虚拟头节点,使所有节点(包括原头节点)都拥有稳定前驱:
dummy_node = Node(0) dummy_node.next = self.head previous_node = dummy_node # 初始指向 dummy,即 head 的前驱
随后执行:
for i in range(start_index):
previous_node = previous_node.next
该循环并非将 previous_node 移至 start_index 位置的节点,而是移动 start_index 步,使其恰好落在索引为 start_index - 1 的节点上(因索引从 0 开始)。例如:
- start_index = 2 → 循环 2 次 → previous_node 最终指向 第 1 个节点(值为 2)的前驱,即值为 1 的节点;
- 此时 current_node = previous_node.next 自然指向 索引 2 处的节点(值为 3),成为反转区间的起点。
✅ 关键理解:previous_node 的作用是锚定反转区间的入口上游,后续所有插入操作都依赖它来“缝合”被抽出的节点。若它停在 start_index 节点上,则无法将新节点正确插入到其前方。
反转过程:三步链表重连(以 reverse_between(2, 3) 为例)
初始链表:1 → 2 → 3 → 4 → 5
目标:反转索引 2~3(即节点 3 和 4),结果应为 1 → 2 → 4 → 3 → 5。
定位锚点
previous_node → 节点 2(索引 1)
current_node → 节点 3(索引 2)-
单次循环执行(end_index - start_index = 1 次):
node_to_move = current_node.next # node_to_move → 4 current_node.next = node_to_move.next # 3 → 5(断开 4) node_to_move.next = previous_node.next # 4 → 3(暂挂载) previous_node.next = node_to_move # 2 → 4(正式接入)
效果:将 4 “剪切”并“粘贴”到 2 之后、3 之前,完成局部反转。
完整可运行示例
class Node:
def __init__(self, value):
self.value = value
self.next = None
class LinkedList:
def __init__(self, value):
self.head = Node(value)
self.length = 1
def append(self, value):
new_node = Node(value)
current = self.head
while current.next:
current = current.next
current.next = new_node
self.length += 1
def reverse_between(self, start_index, end_index):
if self.length <p>使用示例:</p><pre class="brush:php;toolbar:false;">ll = LinkedList(1)
ll.append(2); ll.append(3); ll.append(4); ll.append(5)
ll.print_list() # 1 → 2 → 3 → 4 → 5
ll.reverse_between(2, 3)
ll.print_list() # 1 → 2 → 4 → 3 → 5注意事项与最佳实践
- 索引有效性校验:生产代码中应添加 if start_index = self.length or start_index > end_index: 防御性检查。
- 时间复杂度:O(n),仅需一次遍历定位 + 一次区间遍历反转。
- 空间复杂度:O(1),仅使用常量额外指针。
- dummy 节点不可省略:它消除了对头节点的特殊处理,使逻辑高度一致,是链表原地修改的黄金模式。
掌握 previous_node 的定位本质——它不是目标节点,而是目标区间的“守门人”——你就真正理解了链表区间操作的底层契约。











