题目详解:
1)算法的基本设计思想:先观察 L(a1,a2,a3,⋯,an−2,an−1,an) 和 L′(a1,an,a2,an−1,a3,an−2,⋯),发现 L′ 是由 L 摘取第一个元素,再摘取倒数第一个元素 ⋯ 依次合并而成的。为了方便链表后半段取元素,需要先将 L 后半段原地逆置[题目要求空间复杂度为 O(1),不需要辅助栈],否则每取最后一个结点都需要遍历一次链表。
①先找出链表 L 的中间结点,为此设置两个指针 p 和 q,指针 p 每次走一步,指针 q 每次走两步,当指针 q 到达链尾时,指针 p 正好在链表的中间结点;
②然后将 L 的后半段结点原地逆置。
③从单链表前后两段中依次各取一个结点,按要求重排。
2)算法实现如下:
// 找到链表的中间结点
NODE *findMiddleNode(NODE *head) {
NODE *slow = head;
NODE *fast = head;
while (fast != NULL) {
fast = fast->next;
if (fast != NULL) {
fast = fast->next;
}
slow = slow->next;
}
return slow;
}
// 反转链表
NODE *reverse(NODE *start) {
NODE *p = start;
NODE *q = start->next;
while (q != NULL) {
NODE *tmp = q->next;
q->next = p;
p = q;
q = tmp;
}
return p;
}
// 1. 找到中位结点
// 2. 翻转后一半链表
// 3. 遍历两个链表,依次穿插所有结点
void solve(HEAD *head) {
if (head->next == NULL) {
return;
}
NODE *middle = findMiddleNode(head);
NODE *list2 = reverse(middle);
NODE *list1 = head->next;
// 穿插操作
NODE *p = list1;
NODE *q = list2;
// 退出循环的条件
// 链表长度为奇数:p == q
// 链表长度为偶数:p->next == q
// while (!(p == q || p->next == q))
while (p != q && p->next != q) {
NODE *tmp1 = p->next;
NODE *tmp2 = q->next;
p->next = q;
q->next = tmp1;
p = tmp1;
q = tmp2;
}
}
3)第 1 步找中间结点的时间复杂度为 O(n),第 2 步逆置的时间复杂度为 O(n),第 3 步合并链表的时间复杂度为 O(n),所以该算法的时间复杂度为 O(n)。