Leetcode [92][206]. 反转链表ii&i-输入一个链表,反转链表后,输出链表的所有元素。

 1 /*
 2 struct ListNode {
 3     int val;
 4     struct ListNode *next;
 5     ListNode(int x) :
 6             val(x), next(NULL) {
 7     }
 8 };*/
 9 class Solution {
10 public:
11     ListNode* ReverseList(ListNode* pHead) {
12         ListNode* res=NULL;
13         ListNode* pre=NULL;
14         if(pHead==NULL) return res;
15         while(pHead!=NULL){
16             pre=pHead->next;
17             pHead->next=res;
18             res=pHead;
19             pHead=pre;
20         }
21         return res;
22 
23     }
24 };

 思路:递归反转链表的一部分

实现代码:

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head==nullptr||head->next==nullptr) return head;
        ListNode* last=reverseList(head->next);
        head->next->next=head;
        head->next=nullptr;
        return last;
    }
};

对于递归算法,最重要的就是明确递归函数的定义。具体来说,我们的reverse函数定义是这样的:

输入一个节点head,将「以head为起点」的链表反转,并返回反转之后的头结点

那么输入reverse(head)后,会在这里进行递归:

ListNode* last=reverseList(head->next);

 

 按照定义,这个reverse(head.next)执行完成后,整个链表应该变成了这样:

 

 

 

 

并且根据函数定义,reverse函数会返回反转之后的头结点,我们用变量last接收了。

现在再来看下面的代码:

head.next.next = head;

 

 

接下来进行的操作:

head.next = null;
return last;


 

神不神奇,这样整个链表就反转过来了!递归代码就是这么简洁优雅,不过其中有两个地方需要注意:

1、递归函数要有 base case,也就是这句:

if (head.next == null) return head;

意思是如果链表只有一个节点的时候反转也是它自己,直接返回即可。

2、当链表递归反转之后,新的头节点是last,而之前的head变成了最后一个节点,别忘了链表的末尾要指向 null:

head.next = null;


反转链表ii

 

 思路:递归反转链表的一部分

1、首先实现反转链表的前N个节点

// 将链表的前 n 个节点反转(n <= 链表长度)
ListNode reverseN(ListNode head, int n)

比如说对于下图链表,执行reverseN(head, 3)

 

 解决思路和反转整个链表差不多,只要稍加修改即可:

ListNode successor = null; // 后驱节点

// 反转以 head 为起点的 n 个节点,返回新的头结点
ListNode reverseN(ListNode head, int n) {
    if (n == 1) { 
        // 记录第 n + 1 个节点
        successor = head.next;
        return head;
    }
    // 以 head.next 为起点,需要反转前 n - 1 个节点
    ListNode last = reverseN(head.next, n - 1);

    head.next.next = head;
    // 让反转之后的 head 节点和后面的节点连起来
    head.next = successor;
    return last;
} 

具体的区别:

1、base case 变为n == 1,反转一个元素,就是它本身,同时要记录后驱节点

2、刚才我们直接把head.next设置为 null,因为整个链表反转后原来的head变成了整个链表的最后一个节点。但现在head节点在递归反转之后不一定是最后一个节点了,所以要记录后驱successor(第 n + 1 个节点),反转之后将head连接上。

 

 2、实现反转一部分

首先,如果m == 1,就相当于反转链表开头的n个元素嘛,也就是我们刚才实现的功能:

ListNode reverseBetween(ListNode head, int m, int n) {
    // base case
    if (m == 1) {
        // 相当于反转前 n 个元素
        return reverseN(head, n);
    }
    // ...
}

如果m != 1怎么办?如果我们把head的索引视为 1,那么我们是想从第m个元素开始反转对吧;如果把head.next的索引视为 1 呢?那么相对于head.next,反转的区间应该是从第m - 1个元素开始的;那么对于head.next.next呢……

区别于迭代思想,这就是递归思想,所以我们可以完成代码:

ListNode reverseBetween(ListNode head, int m, int n) {
    // base case
    if (m == 1) {
        return reverseN(head, n);
    }
    // 前进到反转的起点触发 base case
    head.next = reverseBetween(head.next, m - 1, n - 1);
    return head;
}

最终提交代码:

class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) {
        if(left==1)
        {
            return reverseN(head,right);
        }
        head->next=reverseBetween(head->next,left-1,right-1);
        return head;
    }
    ListNode* reverseN(ListNode* head, int n) {
        if(n==1)
        {
            successor=head->next;
            return head;
        }
        ListNode* last=reverseN(head->next,n-1);
        head->next->next=head;
        head->next=successor;
        return last;
    }
    ListNode* successor=NULL;
};

 

posted @ 2015-08-26 21:43  鸭子船长  阅读(344)  评论(0编辑  收藏  举报