题目
将给定的单链表L: L 0→L 1→…→L n-1→L n,
重新排序为: L 0→L n →L 1→L n-1→L 2→L n-2→…
要求使用原地算法,并且不改变节点的值
例如:
对于给定的单链表{1,2,3,4},将其重新排序为{1,4,2,3}.
思路分析
不知大家看懂了题目了吗,相当于是一个链表中,首尾各取一个组成新的链表,不断重复直到整个链表的元素遍历完成。
本题的思路是这样的:
- 用快慢指针找到链表中点,然后分成两条链;
- 对后面的链表进行翻转;
- 将两个链表进行融合;
可以看出,这道题涉及到快慢指针、翻转链表、融合链表等多种知识,因此只要这道题搞清楚,相当于几个知识点都弄清楚了。
但是在运行的过程中遇到一点问题,就是如果用调用函数的方法就是通不过,但是直接写在函数中,就可以通过。看来这道题的要求还是蛮苛刻的?不过重要的是思路!
总的来说,收获最大的就是,各个边界的条件要想清楚,要考虑全面,最后简化下来的代码倒是很简单。这就是代码之美!
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
void reorderList(ListNode *head) {
if (!head || !head->next || !head->next->next)
return;
auto fast = head, slow = head;
while (fast->next && fast->next->next) {
fast = fast->next->next;
slow = slow->next;
}
auto second = slow->next;
slow->next = nullptr;
// 这里翻转
auto p = second;
auto q = second->next;
while (q) {
//auto next = cur->next;
p->next = q->next; //这里的思路是十分巧妙的,相当于是p的位置是没有变的,
//一直是指向头部。
q->next = second;
second = q;
q = p->next; //因为p->next保存了下一个节点,以防止断链。
//second最后就是我们要的头结点,q最后就变成了空节点
}
//开始merge,merge的过程,也要考虑保存后面的节点的问题,然后想清楚是怎样的循环过程。
p = head;
q = second;
while (q) {
head = p->next;
second = q->next;
p->next = q;
q->next = head;
p = head;
q = second;
}
}
};