**(1)设计思想。**目标次序是“头部取一个、尾部取一个”交替排列。单链表不能直接从尾往前走,若每次重新找尾会达到 O(n2)。可以把后半段原地反转,使尾部元素变成可向前依次取的链,再和前半段交错合并。
分三步:用快慢指针找中点,fast 每轮走两步、slow 走一步,把链表分成前半与后半,前半长度等于或比后半多 1;切断两段并反转后半;每次把反转后半的一个结点插到前半当前结点后,继续处理各自原来的下一个结点。只修改 next,不复制数据、不申请新结点。
例如 1→2→3→4→5,分成 1→2→3 与 4→5,后半反转为 5→4,交错插入得 1→5→2→4→3。偶数长度 1→2→3→4 则分成等长两段,得到 1→4→2→3。
**(2)C 实现。**head 是表头结点,不参与数据排列;改链接前保存原 next,避免剩余链断失。
#include <stddef.h>
typedef struct node {
int data;
struct node *next;
} NODE;
void reorder(NODE *head) {
if (head == NULL || head->next == NULL) return;
NODE *slow = head->next, *fast = head->next;
while (fast->next != NULL && fast->next->next != NULL) {
slow = slow->next;
fast = fast->next->next;
}
NODE *p = slow->next;
slow->next = NULL;
NODE *rev = NULL;
while (p != NULL) {
NODE *next = p->next;
p->next = rev;
rev = p;
p = next;
}
p = head->next;
while (rev != NULL) {
NODE *leftNext = p->next, *rightNext = rev->next;
p->next = rev;
rev->next = leftNext;
p = leftNext;
rev = rightNext;
}
}
**(3)正确性与复杂度。**反转后的后半依次为 an,an−1,…,前半保持 a1,a2,…。合并每轮链接一对前、后元素,所以前 k 轮得到 a1,an,…,ak,an−k+1,与目标前缀一致;后半耗尽后若有前半中间结点,留在末尾即可。先切断前半,防止反转与合并形成环;每个原结点最终恰好出现一次。
找中点、反转、合并分别为线性时间,总时间 O(n),只使用固定数量指针,额外空间 O(1)。所有结点一般都需访问,线性时间已经足够高效。空链、只有一项时无需重排,两个结点时次序也保持正确,奇数长度的中间结点最终位于末尾。