美文网首页
LeetCode—— 反转链表 II

LeetCode—— 反转链表 II

作者: Minority | 来源:发表于2020-02-15 14:02 被阅读0次

题目描述

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

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

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
一、CPP
1. 迭代链接反转法:

解题思路:先用一个 for 循环找到第 m 个位置,然后再用一个 for 循环将 m 和 n 之间的元素反转。

  1. 首先,对于链表的问题,根据以往的经验一般都是要建一个dummy node,连上原链表的头结点,这样的话就算头结点变动了,我们还可以通过dummy->next来获得新链表的头结点。
  2. 设置两个指针:pre指向待逆置段的前一个节点(这就是我们为啥要用dummy结点了,万一是结点1开始变换的,pre也可以指向dummy结点)。cur指向待逆置段的第一个节点。
  3. 按照以下方法进行逆置

step1:1 -> 2 -> 3 -> 4 -> 5 -> NULL
step2:1 -> 3 -> 2 -> 4 -> 5 -> NULL
step3:1 -> 4 -> 3 -> 2 -> 5 -> NULL

以交换2和3为例,图示交换过程:

  • 原始顺序,当各个指针指向确定后为:
    对应代码:ListNode *t = cur->next;之前

  • 对应代码: cur->next = t->next;

  • 对应代码: t->next = pre->next;

  • 对应代码: pre->next = t;

  • 下一轮逆置,重新申请t,指向cur后面,pre和cur指向不变


4.刚开始逆置时涉及两个逆置段节点,故逆置轮数只需要进行n-m趟。n-m趟能逆置n-m+1个节点。

时间复杂度:O(n)。
空间复杂度:O(1)。

class Solution {
public:
    ListNode *reverseBetween(ListNode *head, int m, int n) {
        //创建一个头结点
        ListNode *dummy = new ListNode(-1);
        ListNode *pre = dummy;
        dummy->next = head;

        //pre指向要逆置段的前一个
        for (int i = 0; i < m - 1; ++i){
            pre = pre->next;
        } 

        //cur指针指向需要逆置的第一个结点
        ListNode *cur = pre->next;

        //
        for (int i = m; i < n; ++i){
            //一直把t更新为cur后继,以cur为基准
            ListNode *t = cur->next;
            //把t拆下来插到cur前面
            cur->next = t->next;
            t->next = pre->next;
            pre->next = t;
        }

        return dummy->next;
    }
};
2. 单独取出反转法:

解题思路:思路和1也差不多,就是把待逆置段先拆下来,逆置成功后再接上。设计的指针比较多。

  • 初始状态


    指针pre:负责待逆置段逆置之后重新连接
    指针t:负责头插法逆置
    指针tt:负责移动t和连重新接待逆置段的后面的链表
    指针first:负责连接待逆置段的后面的链表,一直指向待逆置的第一个节点
  • 待逆置段完成逆置之后的状态


    再进行简单的拼接即可。

时间复杂度:O(n)。
空间复杂度:O(1)。

class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {

        
        //添加头节点
        ListNode *dummy = new ListNode(-1);
        dummy->next = head;

        //待逆置段的前一个节点pre
        ListNode *pre = dummy;

        //走到待逆置的前一个节点
        for(int i = 0; i< m-1; i++){
            pre = pre->next;
        }

        ListNode *first = pre->next;
        ListNode *t = pre->next;
        ListNode *new_head = new ListNode(-1);
        //断开
        pre->next = NULL;

        // cout<<pre->val<<first->val<<t->val<<new_head->val<<endl; 
        ListNode *tt;
        for(int i = 0;i<(n-m+1); i++){
            
            tt = t->next;
            
            //头插法逆置
            t->next = new_head->next;
            new_head->next = t;

            t = tt;
        }

        //拼接
        pre->next = new_head->next;
        first->next = tt; 

        return dummy->next;
    }
};
3. 递归反转法:

解题思路:递归折磨人,有兴趣可以了解这篇文章,讲的很好。
时间复杂度:O(n)。
空间复杂度:O(n)。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *successor = new ListNode(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;
    }    

    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;

    }
};
二、Java(迭代链接反转法)
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        
        //创建头节点
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        //创建pre指针,并指向合适的位置(移动m-1个位置)
        ListNode pre = dummy;
        for(int i = 0; i<m-1; i++){
            pre = pre.next;
        }
        
        //创建cur,一直指向待逆置段的第一个节点
        ListNode cur = pre.next;

        //逆置
        for(int i = 0; i<n-m; i++){ 
            //t在cur之后
            ListNode t = cur.next;

            cur.next = t.next;
            t.next = pre.next;
            pre.next = t;

        }

        return dummy.next;
    }
}
三、Python(迭代链接反转法)
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
        # 创建头节点
        dummy = ListNode(-1)
        dummy.next = head

        # 创建pre指针,并指向合适的位置(移动m-1个位置)
        pre = dummy
        for i in range(m-1):
            pre = pre.next
        
        # 创建cur,一直指向待逆置段的第一个节点
        cur = pre.next

        # 逆置
        for i in range(n-m):
            t = cur.next

            cur.next = t.next
            t.next = pre.next
            pre.next = t
        
        return dummy.next
四、各语言及算法时间复杂度

相关文章

网友评论

      本文标题:LeetCode—— 反转链表 II

      本文链接:https://www.haomeiwen.com/subject/wnoxfhtx.html