LeetCode(92)ReverseLinkedList2

题目如下:

Reverse a linked list from position m to n. Do it in-place and in one-pass.
For example:
Given 1->2->3->4->5->NULL, m = 2 and n = 4,
return 1->4->3->2->5->NULL.
Note:
Given m, n satisfy the following condition:
1 ≤ m ≤ n ≤ length of list.


分析如下:

把下标范围为[m,n]的节点进行反转,从题例可以看出来,下标从1开始计数。假设,待反转的[m,n]段节点叫做第二段,待反转的[m,n]段节点的左边的节点叫做第一段,待反转的[m,n]段节点的右边的节点叫做第三段。那么,本题最麻烦的地方时,第一段和第三段可能是空的。当m取值为1,n=取值为链表长度length时,这样的情况就会发生。

对于这样的case,我分了情况进行讨论,一共四种情况。这样的代码写出来很长,很容易有bug,基本无法做到20钟之内bugfree。然后网上看了看,有几种解法。

第一种,也是我认为最推荐的,是这里的解法,聪明地简化了代码,只需要讨论第一段是否为空这两种情况,代码很简洁。

第二种,是官网的做法,在head节点之前增加了节点(称作dummy),完全避免了四种情况讨论,这是常见的扬长避短的做法。

第三种,是比较投机取巧的办法,用两个下标,分别指向[m,n]的头m和尾n,先交换节点的值,再向中间靠拢,直到二者相遇为止。这个其实并没有修改节点的链接关系,只是修改了节点的val取值,使得检测的时候呈现的效果和前两种呈现的效果一样。如果题目明确要求不能依靠修改值的方式完成reverse,那么这个方法就挂了。

最好代码:

//我认为最好最简洁的代码,直接偷来了
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *reverseBetween(ListNode *head, int m, int n) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        if (head == NULL)
            return NULL;
            
        ListNode *q = NULL;
        ListNode *p = head;
        for(int i = 0; i < m - 1; i++)
        {
            q = p;
            p = p->next;
        }
        
        ListNode *end = p;
        ListNode *pPre = p;
        p = p->next;
        for(int i = m + 1; i <= n; i++)
        {
            ListNode *pNext = p->next;
            
            p->next = pPre;
            pPre = p;
            p = pNext;
        }
        
        end->next = p;
        if (q)
            q->next = pPre;
        else
            head = pPre;
        
        return head;
    }
};

我的第二版代码:

按照大牛的写法,自己写了一个差不多的。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
ListNode *reverseBetween(ListNode *head, int m, int n) {
        if (head == NULL)
            return NULL;
        ListNode *q = NULL; //q设定为NULL,p设定为head,这样的好处是,接下来可以用(q==NULL)来判断m是否为1,即head节点是否也参与了reverse,即第一段是否为空。
        ListNode *p = head; //
        for(int i = 0; i < m - 1; i++)
        {
            q = p;
            p = p->next;
        }
        
        ListNode* end = p; //第二段反转后的尾节点。
        ListNode* pp=p;
        ListNode* qq=p->next;
        ListNode* rr=NULL;
        
        for(int i=m;i<n;i++){ //[m,n)区间中进行反转,循环结束后,pp指向第二段翻转后的第一个节点。qq和rr都指向pp的下一个节点,也就是第三段的第一个节点(如果第三段存在),或者NULL节点(如果第三段不存在)
            rr=qq->next;
            qq->next=pp;
            pp=qq;
            qq=rr;
        }
        end->next = qq;
        if (q) //如果第一段非空
            q->next = pp;
        else   //如果第一段为空
            head = pp;
        
        return head;
    }

};


我的第一版代码:

这是第一次写时,分了第一段、第二段、第三段这几种情况进行考虑。写得很繁琐。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
ListNode *reverseBetween(ListNode *head, int m, int n) {
        if(m==n)
            return head;
        ListNode* left_neighbor=head;
        ListNode* right_neighbor=head;
        ListNode* p=head;//3个进行reverse需要的变量
        ListNode* q=head;//3个进行reverse需要的变量
        ListNode* r=head;//3个进行reverse需要的变量
        ListNode* mn_new_tail=head;
        
        //找到左邻居,即第一段的最后一个节点
        int count=0;
        if(m==1){
            left_neighbor=NULL;
        }else{
            while(count<m-2){
                left_neighbor=left_neighbor->next;
                count++;
            }
        }
        
        //找到右邻居,即第三段的第一个节点
        if(left_neighbor!=NULL){
            right_neighbor=left_neighbor;
            count=0;
            while(count<n-m+2){
                right_neighbor=right_neighbor->next;
                count++;
            }
        }else{
            right_neighbor=head;
            count=0;
            while(count<n){
                right_neighbor=right_neighbor->next;
                count++;
            }
        }
    
        //开始进行翻转,即翻转第二段,[m,n]这段
        if(left_neighbor!=NULL){
            p=left_neighbor->next;
            mn_new_tail=p;
        }else{
            p=head;
            mn_new_tail=p;
        }
        if(p!=NULL)
            q=p->next;
        if(q!=NULL);
            r=q->next;
        while(q!=right_neighbor){
            r=q->next;
            q->next=p;
            p=q;
            q=r;
        }
        if(left_neighbor!=NULL){
            left_neighbor->next=p;
            mn_new_tail->next=right_neighbor;
            return head;
        }else{
            mn_new_tail->next=right_neighbor;
            return p;
        }
    }
};

update: 2014-12-23

//8ms使用dummy head方式来写,简化代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *reverseBetween(ListNode *head, int m, int n) {
        if(head == NULL || m == n) return head;
        ListNode* dummy_head = new ListNode(-1);
        dummy_head->next = head;
        m++;
        n++;
        int count = n - m;
        ListNode* p_m = dummy_head;
        ListNode* p_m_previous = dummy_head;
        while (m > 1) {
            p_m_previous = p_m;
            p_m = p_m->next;
            m--;
        }
        ListNode* p = p_m;
        ListNode* q = p_m->next;
        ListNode* r = q->next; // 因为 1<=m<=n<=length,所以q必然不为NULL,所以不用判断if(p != NULL)后再 r = q->next;
        //eg: 1 ->2 ->3 -> 4-> 5-> NULL
        //        p   q  r        
        while (count > 0 ) {
            q->next = p;
            p = q;
            q = r;
            if (r != NULL) r = r->next;
            count--;
        }
        p_m_previous->next = p;
        p_m->next = q;
        head = dummy_head->next;
        return head;
    }
};


参考资料:

(1) http://www.cnblogs.com/remlostime/archive/2012/11/18/2776273.html

(2) http://discuss.leetcode.com/questions/267/reverse-linked-list-ii#

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值