递归算法是一种非常有趣且强大的编程技术,它允许方法调用自身来解决问题。使用递归,你可以以简洁的方式解决一些看似复杂的问题,尤其是那些可以被分解为相似子问题的问题。递归在许多算法和数据结构,如排序、搜索和树的遍历中都有应用。

递归算法主要包含两个部分:

  1. 基线条件(Base Case):这是递归停止的条件,没有它,递归将无限进行下去,最终导致堆栈溢出错误。
  2. 递归步骤(Recursive Step):在这一步中,算法会调用自身,通常是在更小或更简单的输入上。

让我们通过一个简单的例子,计算数字n的阶乘(记为n!),来展示递归算法的工作原理。阶乘的定义是从1乘到n,例如5! = 5 * 4 * 3 * 2 * 1 = 120。阶乘函数可以递归定义为:n! = n * (n-1)!,其中1! = 1。

public class Factorial {
    public static void main(String[] args) {
        int number = 5;
        int result = factorial(number);
        System.out.println("The factorial of " + number + " is: " + result);
    }

    public static int factorial(int n) {
        // 基线条件:如果n等于1,就直接返回1
        if (n == 1) {
            return 1;
        }
        // 递归步骤:调用自身,但是n的值减1
        else {
            return n * factorial(n - 1);
        }
    }
}

在这个例子中,factorial 方法通过调用自身来计算阶乘。每次调用时,它将问题规模缩小(即n的值减少),直至达到基线条件n == 1。这时,递归开始逐层返回,最终计算出结果。

递归的关键是正确定义基线条件和递归步骤,确保每次递归调用都在向基线条件靠近,从而避免无限循环。递归算法能够简化代码,但也要注意它可能导致的性能问题,如堆栈溢出或重复计算。在某些情况下,使用循环或记忆化(缓存递归调用的结果)可以提供更好的性能。

题目1:反转字符串中的单词

题目描述:给定一个字符串,逐个翻转字符串中的每个单词。单词的定义是不含空格的字符序列。字符串可能在单词的前后和中间包含一个或多个空格。

示例
输入: “the sky is blue”
输出: “blue is sky the”

解题思路:先整体反转字符串,然后逐个反转每个单词。

Java代码

public class Solution {
    public String reverseWords(String s) {
        // 去除首尾空格,分割字符串
        String[] words = s.trim().split("\\s+");
        StringBuilder reversed = new StringBuilder();
        // 从后向前遍历单词数组,逐个添加到结果中
        for (int i = words.length - 1; i >= 0; i--) {
            reversed.append(words[i]);
            if (i > 0) {
                reversed.append(" ");
            }
        }
        return reversed.toString();
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        String input = "the sky is blue";
        System.out.println(solution.reverseWords(input));
    }
}

题目2:有效的括号

题目描述:给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串,判断字符串是否有效。有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。

示例
输入: “()[]{}”
输出: true

解题思路:使用栈来处理匹配的括号。

Java代码

import java.util.Stack;

public class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for (char c : s.toCharArray()) {
            if (c == '(' || c == '[' || c == '{') {
                stack.push(c);
            } else {
                if (stack.isEmpty()) return false;
                char top = stack.pop();
                if ((c == ')' && top != '(') ||
                    (c == ']' && top != '[') ||
                    (c == '}' && top != '{')) {
                    return false;
                }
            }
        }
        return stack.isEmpty();
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        String input = "()[]{}";
        System.out.println(solution.isValid(input));
    }
}

题目3:爬楼梯

题目描述:假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶

解题思路:动态规划。第n阶可以从第n-1阶或第n-2阶到达。

Java代码

public class Solution {
    public int climbStairs(int n) {
        if (n == 1) return 1;
        int[] dp = new int[n + 1];
        dp[1] = 1;
        dp[2] = 2;
        for (int i = 3; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int n = 3;
        System.out.println(solution.climbStairs(n));
    }
}

这三道题目涵盖了字符串处理、栈的使用以及动态规划,都是面试中非常常见的题型。通过这些题目的学习和练习,可以帮助你准备面试,加深对算法和数据结构的理解。