📌 问题描述
给定一个单链表 L0 → L1 → ... → Ln-1 → Ln
,要求将其重新排列为:
L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → ...
例如:
-
输入:
1 → 2 → 3 → 4 → 5
-
输出:
1 → 5 → 2 → 4 → 3
要求:
-
不能修改节点的值,只能修改节点本身的指向。
-
时间复杂度尽量低,空间复杂度 O(1)。

💡 思路分析
这个问题可以分为 三步走:
-
找到链表中点
使用快慢指针(fast/slow)。当
fast
每次走两步,slow
每次走一步,当fast
到尾时,slow
就在中间节点。 -
反转链表后半部分
将中点后的链表整体反转,这样最后一个节点会变成第一个,方便和前半段交替拼接。
-
合并两条链表
交替拼接前半部分和反转后的后半部分,形成目标顺序。
📝 代码实现(C++)
cpp/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ //思路:找到中间节点,分成两条链表,前链表依次插入反转后的链表 class Solution { public: ListNode* revise(ListNode *L){//反转 if(L==nullptr){ return nullptr; } if(L->next==nullptr) return L; ListNode *head=new ListNode(0); head->next=nullptr; while(L){ ListNode *n=L->next; L->next=head->next; head->next=L; L=n; } return head->next; } void reorderList(ListNode* head) { if(head==nullptr||head->next==nullptr){ return; } ListNode *s=head,*f=head; while(f&&f->next){//找中间节点 f=f->next->next; s=s->next; } ListNode *L2=s->next; s->next=nullptr;//断开两条链表连接防止互相影响 ListNode *front=head; ListNode *tail=revise(L2); while(tail){//插入反转节点 ListNode*tn=tail->next, *fn=front->next; front->next=tail; tail->next=fn; front=fn; tail=tn; } } };
🔎 关键步骤讲解
-
快慢指针找中点
-
slow
每次走一步,fast
每次走两步。 -
最终
slow
停在中点位置。
-
-
反转链表
cpp
ListNode *revise(ListNode *L) {
ListNode *head=new ListNode(0);
while(L){
ListNode *n=L->next;
L->next=head->next;
head->next=L;
L=n;
}
return head->next;
}
这样可以把链表 4 → 5
反转成 5 → 4
。
7. 合并链表
-
先接前半部分
1 → 2 → 3
-
然后把反转后的
5 → 4
插入其中 -
形成最终结果
1 → 5 → 2 → 4 → 3
📚 总结
-
本题考察链表的基础操作:快慢指针、反转链表、链表合并。
-
三步走策略非常经典:
-
找中点
-
反转后半部分
-
交替合并
-
-
这道题在面试中很常见,掌握思路后,类似的链表重排问题都能迎刃而解。 🚀
时间复杂度:O(n)
空间复杂度:O(1)