LeetCode 24 两两交换链表中的节点在 Rust 中实现的核心难点在于所有权与借用检查,必须频繁使用 Option::take() 转移节点所有权来操作指针。以下提供两种主流解法:
解法一:迭代法(虚拟头节点 + take)
这是最经典的写法,思路与 C++/Java 一致,通过 take() 解决所有权问题。
impl Solution {
pub fn swap_pairs(head: Option<Box>) -> Option<Box> {
let mut dummy = Box::new(ListNode { val: 0, next: head });
let mut cur = &mut dummy;
while let Some(mut node1) = cur.next.take() { // 如果只有一个节点,无法配对,直接放回并退出 let Some(mut node2) = node1.next.take() else { cur.next = Some(node1); break; }; // 暂存下一轮的起始节点 let node3 = node2.next.take(); // 执行交换:node1 -> node3, node2 -> node1 node1.next = node3; node2.next = Some(node1); cur.next = Some(node2); // cur 前进两步,到达下一组待交换节点的前一个位置 cur = cur.next.as_mut().unwrap().next.as_mut().unwrap(); } dummy.next }}
执行流程(以 [1,2,3,4] 为例):
初始:dummy -> 1 -> 2 -> 3 -> 4
第一轮交换:
take() 取出 node1=1, node2=2, node3=3->4
重连:node1.next = 3->4, node2.next = 1
dummy -> 2 -> 1 -> 3 -> 4
cur 前进到 node1(值为1)的位置
第二轮交换:
take() 取出 node1=3, node2=4, node3=None
重连:node1.next = None, node2.next = 3
dummy -> 2 -> 1 -> 4 -> 3
返回 dummy.next = [2,1,4,3] ✅
解法二:直接操作引用(无需虚拟头节点)
这种写法更简洁,直接对 head 的可变引用进行操作,省去了虚拟头节点。
impl Solution {
pub fn swap_pairs(mut head: Option<Box>) -> Option<Box> {
let mut current = &mut head;
loop { match current { None => break, Some(current_node) => { match current_node.next.take() { None => break, Some(mut next_node) => { // 重新连接指针 current_node.next = next_node.next.take(); next_node.next = current.take(); *current = Some(next_node); // 前进两步 current = &mut current.as_mut() .unwrap().next.as_mut() .unwrap().next; } } } } } head }}
两种解法对比
维度 解法一(虚拟头节点) 解法二(直接引用)
代码量 稍多 更简洁
可读性 逻辑清晰,易于理解 需要理解 take() + 引用的配合
时间复杂度 O(n) O(n)
空间复杂度 O(1) O(1)
Rust 链表题的关键技巧
- Option::take():将 Option 中的值取出,原位留下 None。这是 Rust 链表操作的核心,本质是所有权转移——因为节点的 next 字段拥有下一个节点的所有权,要重新连接指针就必须先 take() 出来。
- as_mut().unwrap():获取 Option 内部值的可变引用,用于继续遍历和修改。
- 虚拟头节点:统一处理"交换头两个节点"和"交换后续节点"的逻辑,避免单独处理边界条件。
掌握 take() 的用法,Rust 链表题就成功了一大半。
要不要顺带看看递归写法?Rust 里递归处理链表和迭代思路差别挺明显的。