链表-重排链表-中等

描述
给定一个单链表L: L0→L1→…→Ln-1→Ln,
重新排列后为:L0→Ln→L1→Ln-1→L2→Ln-2→…
必须在不改变节点值的情况下进行原地操作。
您在真实的面试中是否遇到过这个题?  
样例
给出链表 1->2->3->4->null,重新排列后为1->4->2->3->null。
挑战

Can you do this in-place without altering the nodes' values?

题目链接

分析

反转以后的链表是,从左往右是,node0->noden->node1->nodenn-1->.....,首先想到是将链表L按照这个规律重新赋值一遍,但题中说不能改变结点的值;然后想到将链表反转然后重新连接起来,如1->2->3->4


按虚线那样走,形成1->4->2->3->3->2->4->1,若是结点的值都不相同,则遇到相同时,就可以断开形成,若是有相同,可先统计结点的个数,然后判断终止条件。这样就需要新建一个链表。所以想到将链表一分为二,反转后半段,然后重新连接起来,依旧是1->2->3->4


这样思路就出来了,分三步走,第一步,将链表一分为二,用到快慢指针;第二步,反转第二部分,反转链表是很重要的根基;第三步,将两链表接起来。

使用快慢指针将链表分成两段,采用这种方式会导致在链表结点个数为奇数的情况下,后半段的个数比前半段多一个。前半段一preSlow维结束,后半段一slow开始。所以在第三步将两子链表连接起来的时候,要注意判断反转以后以newBeg开始的后半段是否已经结束,没有,则连接上剩余部分即可。


针对反转链表,要认真的理解,关键是反转以后,要在新的链表结尾加上next=NULL。


程序


/**
 * 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 *preSlow=NULL;
        ListNode *slow=head,*fast=head;
        while(fast&&fast->next)
        {
            preSlow=slow;
            slow=slow->next;
            fast=fast->next->next;
        }  
        preSlow->next=NULL; //前半段

        //反转后半段
        ListNode *newBeg=slow;
        ListNode *last=newBeg->next;
        while(last)
        {
            ListNode *temp=last->next;
            last->next=newBeg;
            newBeg=last;
            last=temp;
        }
        slow->next=NULL;

        //合并
        fast=head;
        preSlow=NULL;
        while(fast) //注:以前半段为条件
        {
            ListNode *tem=newBeg->next;
            newBeg->next=fast->next;
            fast->next=newBeg;
            fast=newBeg->next;
            preSlow=newBeg;
            newBeg=tem;
        }
        if(newBeg !=NULL)   //因节点个数为奇数时,后段比前段多一个,所以最后要判断
            preSlow->next=newBeg;
    }
};


猜你喜欢

转载自blog.csdn.net/qq_18124075/article/details/80950269