链表反转大集训(LeetCode 206 && LeetCode 25 && LeetCode 92) 使用C++

由于刚接触链表,用起来感觉怪怪的,所以在LeetCode上集中找几个题目集训一下。

一、LeetCode 206-反转链表

题目:

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

1.迭代方法

思路:将旧链表从第一个开始,依次取出,并将其以头结点插入的方式插到新链表中。

要点:

  • 新链表 newHead 要初始化为 NULL
  • 由于每次循环都需要将 旧链表的第一个结点 插入到新链表中,所以要使用 t 将旧链表头节点的next存下来。以便进行  oldHead->next = newHead操作。
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
         ListNode* oldHead = head,*newHead = NULL,*p;
         while(oldHead){
             p = oldHead->next;
             oldHead->next = newHead;
             newHead = oldHead;
             oldHead = p;
         }
         return newHead;
    }
};

2.递归方法

思路:递归用起来还是不太熟练,自己瞎总结的三个要素:本层递归完成的功能、返回的内容及代入内容、终止条件。

在本题中

  • 本层递归需要完成的功能:

有链表n1 → … → nk-1 → nk → nk+1 → … → nm → Ø,假设其余部分已经反转( nk+1 到 nm 已经被反转),我们现在在nk,本层递归我们想让nk+1的下一个结点指向nk,所以就有   nk->next->next = nk;

  • 代入内容及返回内容

一层一层递归,假如要当前是head,则希望当前层能够获得从head->next到NULL的反转,所以代入内容为head->next

同理,每层需要返回将当前结点反转后的新链表。

  • 终止递归的条件:

递归是一层套一层的思想,所以终止条件:找到最后一个元素,head->next == NULL;

此外还要考虑特殊情况,如旧链表是空链表,即 head==NULL, 这两种情况,都会导致递归的结束,都返回head就好,所以可以合并一起来写。

要点:

  • 要写明终止条件和例外情况的返回值
  • 每层递归结束后,都要在尾结点后接入NULL
  • 每次递归只反转一个,多次套用以后,实现多个反转,即由原来的  node2->node3->...->noden->NULL                                                    变为 NULL<-node2<-node3<-...<-noden;但是需要注意的是,并没有改变   node1->node2

node0--->node1--->node2<---node3<---...<---noden

                                   ↓

                                NULL

所以有 node1->next = node2,假如当前获得head为node1,那么当前层需要完成

node->next->next = node;
node->next = NULL;

完整代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head==NULL||head->next==NULL) return head;
        ListNode* newHead = reverseList(head->next);
        head->next->next = head;
        head->next = NULL;
        return newHead;
    }
};

二、LeetCode 25-k个一组翻转链表

题目:

给出一个链表,每 个节点一组进行翻转,并返回翻转后的链表。

是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 的整数倍,那么将最后剩余节点保持原有顺序。

示例 :

给定这个链表:1->2->3->4->5

当 = 2 时,应当返回: 2->1->4->3->5

当 = 3 时,应当返回: 3->2->1->4->5

说明 :

  • 你的算法只能使用常数的额外空间。
  • 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

题解:

1.迭代方法

这个题目就有点难了,先简化问题:

k为变量,实现k个一组翻转 ---> 如果k为常数,实现k个一组翻转 ---> 如果k为结点总量,翻转链表

例外情况:k为1,不翻转。

可以看到,经过三次简化,可以用到上一个题目的思路。在此基础上,那么如果k为常数,则需要:

  • 在传入参数里加一个尾结点,实现子链表反转。
  • 加入计数,如果子链表结点数量达到k,就送去反转,如果不够,就保持原样。
  • 将反转后的链表的尾结点,指向原尾结点的下一个结点(实现部分反转,且链表不中断)

在k为常数的基础上,如果k为变量,那么考虑k=1不反转,k为1以外的正整数,那就进行反转。

注意:在使用t进行遍历链表的同时,进行反转子链表会将t->next修改,所以加入tt暂存。

代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* dummy = new ListNode(0);
        dummy->next = head;//虚拟表头
        ListNode* cur = dummy;//每一段需要反转的链表的表头结点
        ListNode* t = head;//t从开始逐一指向每一个结点,实现遍历
        int count = 0;
        if(k==1 && head==NULL) return head;//k=1或链表为空,不反转
        while(t){//当没有遍历完,继续遍历
            count++;
            if(count == k){//每K个进行反转,此时t的指向为需要反转的子链表的尾结点
                count = 0;
                //头结点插入方式:依次把旧链表中的结点以头结点插入的方式插入到新链表中。
                ListNode* oldHead = cur->next;
                ListNode* newHead = NULL;
                ListNode* wspy = cur->next;//子链表反转之前的头结点,也是反转完成后的尾结点
                ListNode* tt = t->next;//由于反转链表中,会将t->next改变,用tt先记录下来
                ListNode* p;
                while(oldHead!=tt){
                    p = oldHead->next;
                    oldHead->next = newHead;
                    newHead = oldHead;
                    oldHead = p;
                }
                cur->next = newHead;//将反转之后子链表的接入原链表
                wspy->next = tt;   //反转之后的尾结点接上后续链表
                cur = wspy;//更新待反转的链表表头结点
                t = tt;//将t指向原来的t->next,继续遍历
            }
            else t = t->next;
        }
        return dummy->next;
    }
};

2.递归方法

先挖个坑,改日再写递归方法,在这题上花费了不少时间。。

三、LeetCode 92-反转链表II

题目大致相同,但是要求只遍历一次

题目:

反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ m ≤ n ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
/**
 * 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) {
        ListNode* dummy = new ListNode(0);
        ListNode* p = dummy;
        ListNode* tmp,*sta,*tail;
        ListNode* newhead = NULL;
        dummy->next = head;
        int count = 0;
        if(m==n) return head;
        while(count < m-1){
            p = p->next;
            count++;
        }
        tmp = p;
        sta = p->next;
        while(count < n){
            p = sta->next;
            sta->next = newhead;
            newhead = sta;
            sta = p;
            count++;
        }
        tail = tmp->next;
        tmp->next = newhead;
        tail->next = sta;
        return dummy->next;
    }
};

猜你喜欢

转载自blog.csdn.net/jitanban0579/article/details/89406882
今日推荐