• 递归解决反转链表的一部分


    迭代思想:

    先用一个 for 循环找到第 m 个位置,然后再用一个 for 循环将 m 和 n 之间的元素反转。但是我们的递归解法不用一个 for 循环,纯递归实现反转。
    迭代实现思路看起来虽然简单,但是细节问题很多的,反而不容易写对。相反,递归实现就很简洁优美,下面就由浅入深,先从反转整个单链表说起。

    递归反转整个链表

    ListNode reverse(ListNode head) {
        if (head.next == null) return head;
        //last是反转后的头结点
        ListNode last = reverse(head.next);
        
        head.next.next = head;
        head.next = null;
        return last;
    }
    

    上述代码是不是优雅有难以理解?

    1. 想要理解递归代码,首先要明确递归的定义
    2. 遇到递归千万不要把思维跳进递归栈里,你的脑子够想几层递归
    3. 而是要思考该递归执行完成的结果是什么(根据定义)

    语句解析:




    反转前n个节点

    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;
    }
    

    为什么要加successor呢?

    现在 head 节点在递归反转之后不一定是最后一个节点了,所以要记录后驱 successor(第 n + 1 个节点),反转之后将 head 连接上。

    反转链表的一部分

    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;
    }
    
  • 相关阅读:
    【09】绝不在构造和析构过程中调用virtual方法
    【08】别让异常逃离析构函数
    C++ 外部调用private方法
    【07】为多态基类声明virtual析构方法
    C++ 构造过程和析构过程
    理解C# Lazy<T>
    DG
    MongoDB
    sh.status()
    DG
  • 原文地址:https://www.cnblogs.com/treasury/p/12751395.html
Copyright © 2020-2023  润新知