记几条递归与迭代题

99 阅读2分钟

要点备忘

递归的解题要点在于确定好边界范围范围内要做的操作。 在除了《爬楼梯》之外的大部分题中,递归调用行通常是在操作之前,在思路不清前可以截取边界值外的数以此推导三层,但事不过三 (时间不够) 。不宜沉浸在推导中,记住前面两个操作。

LeetCode 70. 爬楼梯

  • 题目:每次可以爬 1 或 2 个台阶,爬到第 n 阶有多少种方法?
  • 递归公式f(n) = f(n-1) + f(n-2)(类似斐波那契数列)。
  • 进阶:用记忆化递归或动态规划优化。
    public int climbStairs(int n){
        if(n==1){
            return 1;
        }
        if(n==2){
            return 2;
        }
        return climbStairs(n-1)+climbStairs(n-2);
    }

以上是代码最少的递归,由于过程中偶尔会递归到重复数字,为了降低消耗(递归调用每次都会创建栈帧),可采用记忆化递归。

public static int climbStairs(int n, int[] memo) {
    if(memo[n]>0){ return memo[n]; }
    if(n==1){ return 1; }
    else if(n==2){ return 2; }
    else{
        memo[n] = climbStairs(n-1, memo) + climbStairs(n-2, memo);
    }
    return climbStairs(n-1,memo)+climbStairs(n-2, memo);
}

public static void main(String[] args) {
    int n=10;
    int[] memo = new int[n+1];
    System.out.println(climbStairs(n,memo));
    System.out.println(Arrays.toString(memo));
}

这道题的迭代写法已属于动态规划范畴,捋得我晕头转向的所以这里先不贴了,本质只是个日常容易查看的记录贴,动态规划有缘再看。

LeetCode 206. 反转链表

  • 题目:反转一个单链表。
  • 递归解法:递归到链表末尾,然后逐层反转指针。
    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;
    }

反转核心:head.next.next 置为当前 head,再设置当前 head.next 为空,将 当前节点与下一节点间的连接断开,返回递归调用的 newHead。

  • 迭代解法:用双指针(经典迭代题)。
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode curr = head;
        while(curr!=null){
            ListNode next = curr.next;
            curr.next = pre;
            pre = curr;
            curr = next;
        }
    }

LeetCode 24. 两两交换链表中的节点

  • 题目:给定链表,两两交换相邻节点。
  • 递归思路head.next = swapPairs(next.next); next.next = head;
    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode newHead =head.next;
        head.next = swapPairs(newHead.next);
        newHead.next = head;
        return newHead;
    }

如前述要点备忘所说,这里的递归调用就是在 “范围内要做的事情” 之前,而 “要做的事情” 就是交换两个节点:head.next = head;,而 head.next 已经被递归执行的结果取代了。最后 return newHead 即可。

  • 迭代解法
    public ListNode swapPairs(ListNode head) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode temp = dummyHead;
        while (temp.next != null && temp.next.next != null) {
            ListNode node1 = temp.next;
            ListNode node2 = temp.next.next;
            temp.next = node2;
            node1.next = node2.next;
            node2.next = node1;
            temp = node1;
        }
        return dummyHead.next;
    }

迭代解法主要是增加了哑结点,避免对没有前驱结点的头结点的特殊处理。