描述

将给定的单链表 L: L0L1Ln−1Ln
重新排序为:L0LnL1Ln−1L2Ln−2
要求使用原地算法,不能只改变节点内部的值,需要对实际的节点进行交换。
数据范围:链表长度0n20000 ,链表中每个节点的值满足0val1000
要求:空间复杂度 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;
    }
};