题目如下:
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#