描述
将给定的单链表 L: L0→L1→…→Ln−1→Ln
重新排序为:L0→Ln→L1→Ln−1→L2→Ln−2→…
要求使用原地算法,不能只改变节点内部的值,需要对实际的节点进行交换。
数据范围:链表长度0≤n≤20000 ,链表中每个节点的值满足0≤val≤1000
重新排序为:L0→Ln→L1→Ln−1→L2→Ln−2→…
要求使用原地算法,不能只改变节点内部的值,需要对实际的节点进行交换。
数据范围:链表长度0≤n≤20000 ,链表中每个节点的值满足0≤val≤1000
要求:空间复杂度 O(n) 并在链表上进行操作而不新建链表,时间复杂度 O(n)
进阶:空间复杂度 O(1) , 时间复杂度 O(n)
问题分析:需要重排链表,最简单粗暴的就是定义一个指针数组,存放每个节点的指针,
但是这样空间复杂度太高了。因为重排是有规律的,从头节点开始每两个中间都加了一个
节点,而这个节点是从后往前的。通过推理可以发现如果后半段逆转,然后让p从头,q从
逆转后的后半段起点开始,逐个往p中间加入q。就可以完美的解决问题。
那么问题就拆分为:1、逆转后半段链表;2、往前半段每两个之间插入后半段的节点。
1、通过快慢指针快速定位到中间节点,然后逆转。
2、再定义两个指针*ptmp,*qtmp,ptmp=p->next,qtmp=q->next。然后就容易操作了。
具体函数看下面的代码,需要注意的地方有注释。
复杂度分析:
时间复杂度:O(n),寻找中间节点O(n/2),反转链表O(n/2),插入节点O(n/2).
空间复杂度:O(1),只需要定义常数个变量。
/**
* 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==NULL||head->next==NULL) return;
ListNode *p,*q,*ptmp,*qtmp;
p=head;q=head;
while(q->next!=NULL&&q->next->next!=NULL)
p=p->next,q=q->next->next;
ptmp=p->next;//记录p的下一个节点作为旋转的开始节点
//当L节点个数为奇数时,是以p作为最后一个节点的,所以需要让这个节点下一个指向null
//当L节点个数为偶数时,是以后半段最后一个为结尾的,而后半段末尾已经指向null。
p->next=NULL;//不管节点个数是偶数还是奇数,这么做都没问题
p=head;
q=reverse(ptmp);//从p->next开始旋转链表
while(q!=NULL) {
ptmp=p->next,qtmp=q->next;//分别保存p,q的下一个节点
p->next=q,q->next=ptmp;//让p,q指针重新指向,也就是插入q节点
p=ptmp,q=qtmp;
}
}
//反转链表
ListNode* reverse(ListNode *N) {
if(N==NULL||N->next==NULL) return N;
ListNode *p=NULL;
ListNode *q=NULL;
while(N!=NULL) {
q=N;
N=N->next;
q->next=p;
p=q;
}
return p;
}
};

京公网安备 11010502036488号