LeetCode(92)ReverseLinkedList2

来源:互联网 发布:我的世界服务器linux 编辑:程序博客网 时间:2024/04/28 20:45

题目如下:

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
原创粉丝点击