LeetCode-143-Reorder List

题目链接

题意:

重构一个单链表,O(1)空间复杂度,O(n)的时间复杂度条件下将链表L1~L2~L3~……~Ln-1~Ln重构为L1~Ln~L2~Ln-1~L3……

思路:

看似是一个双指针的题,一头一尾两个指针向中间合并。然而题目中的单链表这一数据结构使得尾指针无法向前移动,题目难度瞬间增大。

于是,只能利用链表操作来完成。思路也比较清晰,关键就是链表操作细节点很多。化整为零,可以通过三个小操作来实现这个任务:

1.将单链表一分为二

2.将后半部分链表翻转

3.再将两段链表合并

下面详细来说明这三部分

1.这部分主要就是统计一下链表长度,然后用两个指针分别指向链表头和链表中间的位置。对于链表长度为奇数的情况,则将多余的一个节点放在前段。

这里,出现了这个问题最容易出错的地方,那就是当找到第二段节点位置的时候,一定不要忘记将两段链表断开。如果这里忘记断开,后面第三部分链表合并的步骤会出现死循环。

而断链的过程需要链表前段的尾指针,所以还需再遍历链表前段一次。

2.链表翻转,这是一个经典问题,为了方便起见,我单独写了一个函数。

看似一个很简单的操作,然而,实际操作却并非很轻松。

不难发现翻转链表只需要把每个节点的指针指向前一个节点,主要分为两个步骤,断掉后面的链和将链连到前方,虽然这两个操作看似可以一步完成,但却一次性涉及了三个节点,需要用三个指针分别存储这三个节点。每次操作完成后分别将三个节点后移。

这里,又出现了一个很容易出错的地方,那就是链表操作前一定要判空

3.最后一部分就是将两个链表合并,那么双指针分别指向两个链表,轮流将每个指针节点的链指向另一个链表,然后指针后移即可。然而由于单链表的特性,断链后无法找到后半部分的位置,于是还需要额外两个指针来记录每个指针的下一个节点。

代码:

/*
Author Owen_Q
*/

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseLink(ListNode *root)
    {
        //cout << root->val << endl;
        if(root == NULL)
            return NULL;
        //cout << root->val << endl;
        ListNode *a = NULL;
        ListNode *b = root;
        ListNode *c = root->next;
        //cout << a->val << "&" << b->val << "&" << c->val << endl;
        while(b!=NULL)
        {
            b->next = a;
            a = b;
            b = c;
            if(c!=NULL)
                c = c->next;
            /*if(a!=NULL)
                cout << a->val << "&";
            if(b!=NULL)
                cout << b->val << "&" ;
            if(c!=NULL)
                cout << c->val << "&";
            cout << endl;*/
        }
        return a;
    }
    void reorderList(ListNode* head) {
        int len = 0;
        ListNode *t = head;
        while(t!=NULL)
        {
            len++;
            t = t->next;
        }
        if(len>1)
        {
            if(len%2==0) 
                len /= 2;
            else
                len = len/2+1;
            int ll = len-1;
            ListNode *m = head;
            ListNode *n = head;
            //cout << m-> val << endl;
            while(len--)
            {
                /*if(len == 1)
                {
                    ListNode *tt = n->next;
                    n->next = NULL;
                    n = tt;
                }
                else*/
                    n = n->next;
            }
            //cout << n->val << endl;
            n = reverseLink(n);
            cout << n->val << endl;
            t = head;
            while(ll--)
            {
                t = t->next;
            }
            t->next= NULL;
            //cout << n->val << endl;
            while(n!=NULL)
            {
                //cout << "*";
                ListNode *mm = m->next;
                ListNode *nn = n->next;
                //cout << m->val  << "*" << n->val << " " << mm->val << "*" << nn->val ;
                m->next = n;
                n->next = mm;
                m = mm;
                n = nn;
                //cout << m->val  << "*" << n->val << " ";
            }
        }
    }
};

思路(递归版):

此外,再提供一种递归版思路,相对而言实现难度较为简单,也没有很多的坑。

其实这道题很容易就可以想到递归分治来解决。

因为,重构一个L1~……~Ln的链表,完全可以将收尾结点提出,递归为:L1~Ln~(重构一个L2~……~Ln-1的链表)。

只要确保每次递归复杂度为O(1),即可达到最终O(n)的时间复杂度。

不难发现,对于单链表,最消耗时间的即为尾结点的寻找,如果能优化掉这部分,问题将顺利解决。

于是,问题简化为将head~A……B~tail[~nextnode]转化为head~tail~A……B[~nextnode],这里的A……B即为递归简化的子问题,这里假设A非空,但可能为单结点。如果每次递归能将B结点返回,那么尾结点(tail)即为B结点的下一个结点,完美解决。

最后考虑一下递归边界的问题,由于存在边界问题,因此需要计算一下当前递归所剩长度,奇数长度递归边界为单节点,偶数长度递归边界为双结点(显然,此种情况下,尾结点为双结点的后一个结点)。

代码:

class Solution {
public:
    ListNode* recursiveLink(ListNode *head,int len)
    {
        if(len==1)//single link
            return head;
        else if(len==2)//double link
            return head->next;
        else
        {
            ListNode* B = recursiveLink(head->next,len-2);
            ListNode* A = head->next;
            ListNode* tail = B->next;
            ListNode* nextnode = tail->next;
            head->next = tail;
            tail->next = A;
            B->next = nextnode;
            return B;
        }
    }
    
    void reorderList(ListNode* head) {
        ListNode *tmp = head;
        int len = 0;
        while(tmp!=NULL)
        {
            len++;
            tmp = tmp->next;
        }
        if(len>0)
            recursiveLink(head,len);
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值