1使用Java语言,实现斐波那契数列。

斐波那契数列(Fibonacci sequence)是一个经典的数学序列,其中每个数字是前两个数字的和。通常,斐波那契数列从01开始,即:
F(0) = 0, F(1) = 1
F(n) = F(n-1) + F(n-2) for n >= 2
Java中,我们可以使用递归或迭代的方法来实现斐波那契数列。下面分别展示这两种方法的实现。

1.1递归实现

递归实现是最直观的方法,但效率较低,特别是对于较大的n值,因为它会重复计算许多子问题。
public class Fibonacci {
    // 递归方法
    public static int fibonacciRecursive(int n) {
        if (n <= 1) {
            return n;
        }
        return fibonacciRecursive(n - 1) + fibonacciRecursive(n - 2);
    }

    public static void main(String[] args) {
        int n = 10; // 示例:计算第10个斐波那契数
        System.out.println("Fibonacci number at position " + n + " is: " + fibonacciRecursive(n));
    }
}

1.2迭代实现

迭代实现比递归更高效,因为它避免了重复计算。
public class Fibonacci {
    // 迭代方法
    public static int fibonacciIterative(int n) {
        if (n <= 1) {
            return n;
        }
        int a = 0, b = 1;
        for (int i = 2; i <= n; i++) {
            int temp = a + b;
            a = b;
            b = temp;
        }
        return b;
    }

    public static void main(String[] args) {
        int n = 10; // 示例:计算第10个斐波那契数
        System.out.println("Fibonacci number at position " + n + " is: " + fibonacciIterative(n));
    }
}

1.3动态规划实现

动态规划是一种优化技术,通过存储已经计算过的结果来避免重复计算。
import java.util.HashMap;
import java.util.Map;

public class Fibonacci {
    private static Map<Integer, Integer> cache = new HashMap<>();

    // 动态规划方法
    public static int fibonacciDP(int n) {
        if (n <= 1) {
            return n;
        }
        if (cache.containsKey(n)) {
            return cache.get(n);
        }
        int result = fibonacciDP(n - 1) + fibonacciDP(n - 2);
        cache.put(n, result);
        return result;
    }

    public static void main(String[] args) {
        int n = 10; // 示例:计算第10个斐波那契数
        System.out.println("Fibonacci number at position " + n + " is: " + fibonacciDP(n));
    }
}

1.4总结

以上三种方法各有优缺点:

2使用Java语言,解决最长公共子序列(LCS)问题。

最长公共子序列(LCS,Longest Common Subsequence)问题是一个经典的动态规划问题。给定两个字符串,找出它们的最长公共子序列。

2.1问题描述

给定两个字符串 XY,找到它们的最长公共子序列。例如:

2.2动态规划解法

我们可以使用一个二维数组 dp 来存储子问题的解。其中 dp[i][j] 表示字符串 X 的前 i 个字符和字符串 Y 的前 j 个字符的最长公共子序列的长度。

2.2.1状态转移方程

  1. 如果 X[i-1] == Y[j-1],则 dp[i][j] = dp[i-1][j-1] + 1
  2. 如果 X[i-1] != Y[j-1],则 dp[i][j] = max(dp[i-1][j], dp[i][j-1])

2.2.2初始条件

2.3Java实现

下面是用Java语言实现的代码:
public class LCS {
    // 方法:计算两个字符串的最长公共子序列长度
    public static int longestCommonSubsequence(String X, String Y) {
        int m = X.length();
        int n = Y.length();
        int[][] dp = new int[m + 1][n + 1];

        // 填充dp数组
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (X.charAt(i - 1) == Y.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }

        return dp[m][n];
    }

    // 方法:构建最长公共子序列
    public static String buildLCS(String X, String Y) {
        int m = X.length();
        int n = Y.length();
        int[][] dp = new int[m + 1][n + 1];

        // 填充dp数组
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (X.charAt(i - 1) == Y.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }

        // 回溯构建LCS字符串
        StringBuilder lcs = new StringBuilder();
        int i = m, j = n;
        while (i > 0 && j > 0) {
            if (X.charAt(i - 1) == Y.charAt(j - 1)) {
                lcs.append(X.charAt(i - 1));
                i--;
                j--;
            } else if (dp[i - 1][j] > dp[i][j - 1]) {
                i--;
            } else {
                j--;
            }
        }

        return lcs.reverse().toString();
    }

    public static void main(String[] args) {
        String X = "ABCBDAB";
        String Y = "BDCAB";
        
        int length = longestCommonSubsequence(X, Y);
        System.out.println("Length of LCS: " + length); // 输出:4
        
        String lcs = buildLCS(X, Y);
        System.out.println("LCS: " + lcs); // 输出:BCAB 或 BDAB
    }
}

2.4解释

  1. longestCommonSubsequence 方法:计算并返回两个字符串的最长公共子序列的长度。
  2. buildLCS 方法:不仅计算最长公共子序列的长度,还构建并返回实际的最长公共子序列。
  3. 主方法:测试上述方法,输出结果。
通过这个实现,你可以解决最长公共子序列的问题,并且能够获取到具体的最长公共子序列字符串。

3使用Java语言,解决背包问题(0/1背包问题)。

背包问题(0/1背包问题)是经典的动态规划问题之一。在这个问题中,给定一组物品,每个物品有一个重量和一个价值,目标是选择一些物品放入一个容量有限的背包中,使得背包中的总价值最大。

3.1问题描述

假设有 n 个物品,每个物品有一个重量 w[i] 和一个价值 v[i]。背包的容量为 W。我们需要找到一种选择方式,使得在不超过背包容量的前提下,背包中的总价值最大。

3.2动态规划解法

我们可以使用动态规划来解决这个问题。定义一个二维数组 dp,其中 dp[i][j] 表示前 i 个物品在容量为 j 的背包中的最大价值。

3.2.1状态转移方程

  1. 如果第 i 个物品不放入背包:dp[i][j] = dp[i-1][j]
  2. 如果第 i 个物品放入背包:dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])

3.2.2初始条件

3.3Java实现

下面是用Java语言实现的代码:
public class Knapsack {
    // 方法:解决0/1背包问题
    public static int knapsack(int[] weights, int[] values, int W) {
        int n = weights.length;
        int[][] dp = new int[n + 1][W + 1];

        // 填充dp数组
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= W; j++) {
                if (weights[i - 1] <= j) {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1]);
                } else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }

        return dp[n][W];
    }

    public static void main(String[] args) {
        int[] weights = {1, 2, 3}; // 物品的重量
        int[] values = {6, 10, 12}; // 物品的价值
        int W = 5; // 背包的容量

        int maxValue = knapsack(weights, values, W);
        System.out.println("Maximum value in the knapsack: " + maxValue); // 输出:22
    }
}

3.4解释

  1. knapsack 方法:计算并返回在给定容量的背包中能够获得的最大价值。
  2. 主方法:测试上述方法,输出结果。
通过这个实现,你可以解决0/1背包问题,并且能够获取到在给定容量的背包中能够获得的最大价值。

4使用Java语言,解决编辑距离问题(如莱文斯坦距离)。

编辑距离(Levenshtein Distance)问题,也称为莱文斯坦距离,是计算两个字符串之间的最小编辑操作次数的问题。允许的编辑操作包括插入一个字符、删除一个字符和替换一个字符。

4.1问题描述

给定两个字符串 str1str2,计算将 str1 转换为 str2 所需的最小编辑操作次数。

4.2动态规划解法

我们可以使用动态规划来解决这个问题。定义一个二维数组 dp,其中 dp[i][j] 表示将 str1 的前 i 个字符转换为 str2 的前 j 个字符所需的最小编辑操作次数。

4.2.1状态转移方程

  1. 如果 str1[i-1] == str2[j-1],则 dp[i][j] = dp[i-1][j-1]
  2. 如果 str1[i-1] != str2[j-1],则 dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1

4.2.2初始条件

4.3Java实现

下面是用Java语言实现的代码:
public class EditDistance {
    // 方法:计算两个字符串的编辑距离
    public static int levenshteinDistance(String str1, String str2) {
        int m = str1.length();
        int n = str2.length();
        int[][] dp = new int[m + 1][n + 1];

        // 初始化dp数组
        for (int i = 0; i <= m; i++) {
            dp[i][0] = i;
        }
        for (int j = 0; j <= n; j++) {
            dp[0][j] = j;
        }

        // 填充dp数组
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
                }
            }
        }

        return dp[m][n];
    }

    public static void main(String[] args) {
        String str1 = "kitten";
        String str2 = "sitting";
        
        int distance = levenshteinDistance(str1, str2);
        System.out.println("Edit distance between '" + str1 + "' and '" + str2 + "': " + distance); // 输出:3
    }
}

4.4解释

  1. levenshteinDistance 方法:计算并返回两个字符串之间的编辑距离。
  2. 主方法:测试上述方法,输出结果。
通过这个实现,你可以解决编辑距离问题,并且能够获取到将一个字符串转换为另一个字符串所需的最小编辑操作次数。

5使用Java语言,实现最大子数组和问题(连续子数组的最大和)。

最大子数组和问题(Maximum Subarray Sum)是经典的算法问题之一,通常使用Kadane's Algorithm来解决。该算法的时间复杂度为O(n),非常高效。

5.1问题描述

给定一个整数数组 nums,找到一个具有最大和的连续子数组(至少包含一个元素),返回其最大和。

5.2Kadane's Algorithm

Kadane's Algorithm的核心思想是通过一次遍历数组来找到最大子数组和。具体步骤如下:
  1. 初始化两个变量:maxCurrentmaxGlobalmaxCurrent 用于记录当前子数组的最大和,maxGlobal 用于记录全局最大和。
  2. 遍历数组,对于每个元素 nums[i]
    • 更新 maxCurrentmax(nums[i], maxCurrent + nums[i])
    • 如果 maxCurrent 大于 maxGlobal,则更新 maxGlobal
  3. 最终,maxGlobal 就是最大子数组和。

5.3Java实现

下面是用Java语言实现的代码:
public class MaxSubArraySum {
    // 方法:计算最大子数组和
    public static int maxSubArray(int[] nums) {
        if (nums == null || nums.length == 0) {
            throw new IllegalArgumentException("Input array cannot be null or empty");
        }

        int maxCurrent = nums[0];
        int maxGlobal = nums[0];

        for (int i = 1; i < nums.length; i++) {
            maxCurrent = Math.max(nums[i], maxCurrent + nums[i]);
            if (maxCurrent > maxGlobal) {
                maxGlobal = maxCurrent;
            }
        }

        return maxGlobal;
    }

    public static void main(String[] args) {
        int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
        int result = maxSubArray(nums);
        System.out.println("Maximum subarray sum is: " + result); // 输出:6
    }
}

5.4解释

  1. maxSubArray 方法:计算并返回最大子数组和。首先检查输入数组是否为空或长度为0,如果是则抛出异常。然后初始化 maxCurrentmaxGlobal 为数组的第一个元素。接着遍历数组,从第二个元素开始,更新 maxCurrentmaxGlobal
  2. 主方法:测试上述方法,输出结果。
通过这个实现,你可以解决最大子数组和问题,并且能够获取到最大子数组和的值。

6使用Java语言,解决不同路径问题(如机器人在网格中移动的不同路径数)。

不同路径问题(Unique Paths Problem)是一个经典的动态规划问题,通常用于计算机器人在网格中从左上角移动到右下角的不同路径数。假设机器人只能向右或向下移动。

6.1问题描述

给定一个 m x n 的网格,机器人从左上角 (0, 0) 开始移动到右下角 (m-1, n-1)。每次只能向右或向下移动一步。问有多少条不同的路径?

6.2动态规划解法

我们可以使用动态规划来解决这个问题。定义一个二维数组 dp,其中 dp[i][j] 表示从起点 (0, 0) 到达位置 (i, j) 的不同路径数。

6.2.1状态转移方程

  1. 如果机器人在第一行或第一列,它只能一直向右或一直向下移动,因此 dp[i][0] = 1dp[0][j] = 1
  2. 对于其他位置 (i, j),机器人可以从上方 (i-1, j) 或左方 (i, j-1) 到达,因此 dp[i][j] = dp[i-1][j] + dp[i][j-1]

6.2.2初始条件

6.3Java实现

下面是用Java语言实现的代码:
public class UniquePaths {
    // 方法:计算机器人从左上角到右下角的不同路径数
    public static int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];

        // 初始化第一行和第一列
        for (int i = 0; i < m; i++) {
            dp[i][0] = 1;
        }
        for (int j = 0; j < n; j++) {
            dp[0][j] = 1;
        }

        // 填充dp数组
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }

        return dp[m-1][n-1];
    }

    public static void main(String[] args) {
        int m = 3; // 行数
        int n = 7; // 列数
        int result = uniquePaths(m, n);
        System.out.println("Number of unique paths: " + result); // 输出:28
    }
}

6.4解释

  1. uniquePaths 方法:计算并返回从左上角到右下角的不同路径数。首先初始化第一行和第一列为1,然后通过双重循环填充 dp 数组。
  2. 主方法:测试上述方法,输出结果。
通过这个实现,你可以解决不同路径问题,并且能够获取到机器人从左上角移动到右下角的不同路径数。

7使用Java语言,解决爬楼梯问题(斐波那契数列的变种)。

爬楼梯问题(Climbing Stairs Problem)是经典的动态规划问题,通常用于计算到达顶部的不同方法数。假设你正在爬一个 n 阶的楼梯,每次你可以爬1阶或2阶。问有多少种不同的方法可以爬到楼梯的顶部?
这个问题实际上是斐波那契数列的一个变种。我们可以使用动态规划来解决这个问题。

7.1问题描述

给定一个整数 n,表示楼梯的总阶数。每次你可以爬1阶或2阶。问有多少种不同的方法可以爬到楼梯的顶部?

7.2动态规划解法

我们可以定义一个数组 dp,其中 dp[i] 表示到达第 i 阶的方法数。

7.2.1状态转移方程

  1. 如果在第 i 阶,可以从第 i-1 阶爬1阶上来,也可以从第 i-2 阶爬2阶上来。因此,dp[i] = dp[i-1] + dp[i-2]
  2. 初始条件:dp[0] = 1(在起点),dp[1] = 1(只有一种方法)。

7.3Java实现

下面是用Java语言实现的代码:
public class ClimbingStairs {
    // 方法:计算到达顶部的不同方法数
    public static int climbStairs(int n) {
        if (n <= 1) {
            return 1;
        }

        int[] dp = new int[n + 1];
        dp[0] = 1; // 起点
        dp[1] = 1; // 第一阶

        for (int i = 2; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }

        return dp[n];
    }

    public static void main(String[] args) {
        int n = 5; // 楼梯总阶数
        int result = climbStairs(n);
        System.out.println("Number of ways to climb " + n + " stairs: " + result); // 输出:8
    }
}

7.4解释

  1. climbStairs 方法:计算并返回到达顶部的不同方法数。首先处理特殊情况 n <= 1,然后初始化 dp 数组,并填充它。
  2. 主方法:测试上述方法,输出结果。
通过这个实现,你可以解决爬楼梯问题,并且能够获取到到达顶部的不同方法数。

8使用Java语言,实现最小路径和问题(在一个二维网格中找到从左上角到右下角的最小路径和)。

最小路径和问题(Minimum Path Sum Problem)是一个经典的动态规划问题,通常用于计算在一个二维网格中从左上角到右下角的最小路径和。假设你在一个 m x n 的网格中,每个单元格包含一个非负整数,每次只能向右或向下移动一步。问从左上角到右下角的路径上的数字之和最小的是多少?

8.1问题描述

给定一个 m x n 的网格 grid,其中 grid[i][j] 表示位于第 i 行第 j 列的单元格的值。你需要找到一条从左上角到右下角的路径,使得路径上的所有数字之和最小。

8.2动态规划解法

我们可以使用动态规划来解决这个问题。定义一个二维数组 dp,其中 dp[i][j] 表示从左上角 (0, 0) 到达位置 (i, j) 的最小路径和。

8.2.1状态转移方程

  1. 如果机器人在第一行或第一列,它只能一直向右或一直向下移动,因此 dp[i][0] = dp[i-1][0] + grid[i][0]dp[0][j] = dp[0][j-1] + grid[0][j]
  2. 对于其他位置 (i, j),机器人可以从上方 (i-1, j) 或左方 (i, j-1) 到达,因此 dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + grid[i][j]

8.2.2初始条件

8.3Java实现

下面是用Java语言实现的代码:
public class MinPathSum {
    // 方法:计算从左上角到右下角的最小路径和
    public static int minPathSum(int[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) {
            return 0;
        }

        int m = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m][n];

        // 初始化第一行和第一列
        dp[0][0] = grid[0][0];
        for (int i = 1; i < m; i++) {
            dp[i][0] = dp[i-1][0] + grid[i][0];
        }
        for (int j = 1; j < n; j++) {
            dp[0][j] = dp[0][j-1] + grid[0][j];
        }

        // 填充dp数组
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
            }
        }

        return dp[m-1][n-1];
    }

    public static void main(String[] args) {
        int[][] grid = {
            {1, 3, 1},
            {1, 5, 1},
            {4, 2, 1}
        };
        int result = minPathSum(grid);
        System.out.println("Minimum path sum: " + result); // 输出:7
    }
}

8.4解释

  1. minPathSum 方法:计算并返回从左上角到右下角的最小路径和。首先处理特殊情况,然后初始化 dp 数组,并填充它。
  2. 主方法:测试上述方法,输出结果。
通过这个实现,你可以解决最小路径和问题,并且能够获取到从左上角到右下角的最小路径和。

9使用Java语言,解决股票买卖问题(给定一个价格数组,找到最大利润的交易方案)。

股票买卖问题(Stock Buy and Sell Problem)是一个经典的动态规划问题,通常用于计算在给定价格数组中,通过一次买入和一次卖出可以获得的最大利润。

9.1问题描述

给定一个数组 prices,其中 prices[i] 表示第 i 天的股票价格。你需要找到一种交易方案,使得你能够获得最大利润。你可以进行一次买入和一次卖出操作。注意,你不能同时参与多笔交易(即你必须在再次买入之前卖出股票)。

9.2动态规划解法

我们可以使用动态规划来解决这个问题。定义两个变量:
遍历价格数组,对于每一天的价格,更新 minPricemaxProfit

9.2.1状态转移方程

  1. minPrice = Math.min(minPrice, prices[i]):更新到目前为止的最低价格。
  2. maxProfit = Math.max(maxProfit, prices[i] - minPrice):更新到目前为止的最大利润。

9.3Java实现

下面是用Java语言实现的代码:
public class StockBuySell {
    // 方法:计算最大利润
    public static int maxProfit(int[] prices) {
        if (prices == null || prices.length < 2) {
            return 0; // 无法进行交易
        }

        int minPrice = Integer.MAX_VALUE;
        int maxProfit = 0;

        for (int price : prices) {
            if (price < minPrice) {
                minPrice = price; // 更新最低价格
            } else if (price - minPrice > maxProfit) {
                maxProfit = price - minPrice; // 更新最大利润
            }
        }

        return maxProfit;
    }

    public static void main(String[] args) {
        int[] prices = {7, 1, 5, 3, 6, 4};
        int result = maxProfit(prices);
        System.out.println("Maximum profit: " + result); // 输出:5
    }
}

9.4解释

  1. maxProfit 方法:计算并返回最大利润。首先处理特殊情况,如果价格数组为空或长度小于2,则无法进行交易,返回0。然后初始化 minPrice 为最大整数值,maxProfit0。遍历价格数组,更新 minPricemaxProfit
  2. 主方法:测试上述方法,输出结果。
通过这个实现,你可以解决股票买卖问题,并且能够获取到最大利润的交易方案。

10使用Java语言,解决打家劫舍问题(不能抢劫相邻的房子,求最大抢劫金额)。

打家劫舍问题(House Robber Problem)是一个经典的动态规划问题,通常用于计算在一排房屋中,不能抢劫相邻的房子的情况下,能够获得的最大抢劫金额。

10.1问题描述

给定一个数组 nums,其中 nums[i] 表示第 i 个房子中的金额。你需要找到一种抢劫方案,使得你能够获得最大金额,但不能抢劫相邻的房子。

10.2动态规划解法

我们可以使用动态规划来解决这个问题。定义一个数组 dp,其中 dp[i] 表示从第 0 个房子到第 i 个房子能获得的最大抢劫金额。

10.2.1状态转移方程

  1. 如果只有一个房子,那么最大抢劫金额就是该房子的金额:dp[0] = nums[0]
  2. 如果有两个房子,那么最大抢劫金额是这两个房子金额中的较大值:dp[1] = Math.max(nums[0], nums[1])
  3. 对于更多的房子,如果选择抢劫第 i 个房子,那么就不能抢劫第 i-1 个房子,因此 dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i])

10.3Java实现

下面是用Java语言实现的代码:
public class HouseRobber {
    // 方法:计算最大抢劫金额
    public static int rob(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0; // 没有房子可以抢劫
        }
        if (nums.length == 1) {
            return nums[0]; // 只有一个房子
        }

        int n = nums.length;
        int[] dp = new int[n];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0], nums[1]);

        for (int i = 2; i < n; i++) {
            dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i]);
        }

        return dp[n-1];
    }

    public static void main(String[] args) {
        int[] nums = {2, 7, 9, 3, 1};
        int result = rob(nums);
        System.out.println("Maximum amount: " + result); // 输出:12
    }
}

10.4解释

  1. rob 方法:计算并返回最大抢劫金额。首先处理特殊情况,如果数组为空或长度为0,则返回0。如果数组长度为1,则返回第一个元素的值。然后初始化 dp 数组,并填充它。
  2. 主方法:测试上述方法,输出结果。
通过这个实现,你可以解决打家劫舍问题,并且能够获取到最大抢劫金额的方案。

11使用Java语言,实现硬币兑换问题(给定不同面额的硬币,求最少的硬币数量组成某个金额)。

硬币兑换问题(Coin Change Problem)是一个经典的动态规划问题,通常用于计算使用最少数量的硬币来组成某个金额。

11.1问题描述

给定不同面额的硬币和一个总金额,你需要计算出最少的硬币数量来组成这个金额。如果无法组成该金额,则返回 -1。

11.2动态规划解法

我们可以使用动态规划来解决这个问题。定义一个数组 dp,其中 dp[i] 表示组成金额 i 所需的最少硬币数量。

11.2.1状态转移方程

  1. 如果金额为0,那么所需硬币数量为0:dp[0] = 0
  2. 对于其他金额,初始化为一个较大的值(例如 amount + 1),表示初始状态下无法组成该金额:dp[i] = amount + 1
  3. 对于每个金额 i,遍历所有硬币面额 coin,更新 dp[i]dp[i] = Math.min(dp[i], dp[i - coin] + 1)

11.3Java实现

下面是用Java语言实现的代码:
public class CoinChange {
    // 方法:计算最少硬币数量
    public static int coinChange(int[] coins, int amount) {
        if (amount < 0) return -1; // 无效金额
        int[] dp = new int[amount + 1];
        dp[0] = 0; // 组成金额0需要0个硬币

        for (int i = 1; i <= amount; i++) {
            dp[i] = amount + 1; // 初始化为一个较大的值
            for (int coin : coins) {
                if (i >= coin) {
                    dp[i] = Math.min(dp[i], dp[i - coin] + 1);
                }
            }
        }

        return dp[amount] > amount ? -1 : dp[amount];
    }

    public static void main(String[] args) {
        int[] coins = {1, 2, 5};
        int amount = 11;
        int result = coinChange(coins, amount);
        System.out.println("Minimum coins needed: " + result); // 输出:3
    }
}

11.4解释

  1. coinChange 方法:计算并返回组成指定金额所需的最少硬币数量。首先处理特殊情况,如果金额小于0,则返回-1。然后初始化 dp 数组,并填充它。
  2. 主方法:测试上述方法,输出结果。
通过这个实现,你可以解决硬币兑换问题,并且能够获取到组成某个金额所需的最少硬币数量。

12使用Java语言,解决跳跃游戏问题(判断能否到达数组末尾)。

跳跃游戏问题是一个经典的算法问题,通常可以通过贪心算法来解决。给定一个非负整数数组 nums,你最初位于数组的第一个位置。数组中的每个元素代表你在那个位置可以跳跃的最大长度。你的目标是判断是否能够到达数组的最后一个位置。
以下是使用Java语言实现的解决方案:
public class JumpGame {
    public boolean canJump(int[] nums) {
        // 初始化最远能跳到的位置为0
        int maxReach = 0;
        
        // 遍历数组中的每一个元素
        for (int i = 0; i < nums.length; i++) {
            // 如果当前位置已经超出了最远能跳到的位置,则无法继续前进
            if (i > maxReach) {
                return false;
            }
            // 更新最远能跳到的位置
            maxReach = Math.max(maxReach, i + nums[i]);
            // 如果最远能跳到的位置已经大于或等于数组的最后一个位置,返回true
            if (maxReach >= nums.length - 1) {
                return true;
            }
        }
        
        // 如果循环结束后仍未能到达数组末尾,返回false
        return false;
    }

    public static void main(String[] args) {
        JumpGame game = new JumpGame();
        int[] nums = {2, 3, 1, 1, 4};
        System.out.println(game.canJump(nums)); // 输出: true
        
        int[] nums2 = {3, 2, 1, 0, 4};
        System.out.println(game.canJump(nums2)); // 输出: false
    }
}

12.1解释

  1. 变量初始化:我们用 maxReach 来记录当前能跳到的最远位置。初始值为0,因为从第一个位置开始。
  2. 遍历数组:我们遍历数组中的每一个元素。
    • 如果当前索引 i 超过了 maxReach,说明当前位置是无法到达的,直接返回 false
    • 否则,更新 maxReach 为当前位置和 maxReach 之间的最大值。
    • 如果 maxReach 已经大于或等于数组的最后一个位置,说明可以到达末尾,返回 true
  3. 返回结果:如果遍历完数组后仍未能到达末尾,返回 false
这个算法的时间复杂度是 O(n),其中 n 是数组的长度,因为我们只需要遍历一次数组。空间复杂度是 O(1),因为我们只使用了常数级别的额外空间。

13使用Java语言,实现三角形最小路径和问题(给定一个三角形数组,求从顶到底的最小路径和)。

三角形最小路径和问题是一个经典的动态规划问题。给定一个三角形数组,求从顶到底的最小路径和。我们可以使用自底向上的动态规划方法来解决这个问题。
以下是Java语言实现:
import java.util.List;

public class TriangleMinPathSum {
    public int minimumTotal(List<List<Integer>> triangle) {
        if (triangle == null || triangle.size() == 0) {
            return 0;
        }

        // 获取三角形的高度
        int n = triangle.size();
        
        // dp数组用于存储到达每个位置的最小路径和
        int[] dp = new int[n];
        
        // 初始化dp数组为三角形最底层的值
        for (int i = 0; i < n; i++) {
            dp[i] = triangle.get(n - 1).get(i);
        }
        
        // 从倒数第二层开始往上计算
        for (int i = n - 2; i >= 0; i--) {
            for (int j = 0; j <= i; j++) {
                // 状态转移方程:dp[j] = min(dp[j], dp[j + 1]) + triangle.get(i).get(j)
                dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);
            }
        }
        
        // 最终结果在dp[0]中
        return dp[0];
    }

    public static void main(String[] args) {
        TriangleMinPathSum solution = new TriangleMinPathSum();
        List<List<Integer>> triangle = List.of(
            List.of(2),
            List.of(3, 4),
            List.of(6, 5, 7),
            List.of(4, 1, 8, 3)
        );
        int result = solution.minimumTotal(triangle);
        System.out.println("Minimum path sum is: " + result); // 输出: 11
    }
}

13.1解释

  1. 输入检查:首先检查输入是否为空或大小为0,如果是则返回0。
  2. 初始化dp数组dp数组用于存储到达每个位置的最小路径和。我们初始化dp数组为三角形最底层的值。
  3. 自底向上计算:从倒数第二层开始,逐层向上计算每个位置的最小路径和。状态转移方程为 dp[j] = min(dp[j], dp[j + 1]) + triangle.get(i).get(j)
  4. 返回结果:最终结果存储在dp[0]中,即从顶到底的最小路径和。
通过这种方式,我们可以高效地计算出从顶到底的最小路径和。

14使用Java语言,解决石子游戏问题(两个玩家轮流取石子,求先手是否必胜)。

石子游戏问题是一个经典的动态规划问题。给定一堆石子,两个玩家轮流从堆中取走至少一个石子,但不能超过当前堆中的石子数的一半。求先手是否必胜。

14.1问题描述

假设有 n 个石子,两个玩家轮流从堆中取石子,每次可以取走至少一个石子且不能超过当前堆中的石子数的一半。问先手玩家是否有必胜策略。

14.2分析

这个问题可以通过动态规划来解决。我们定义一个布尔数组 dp,其中 dp[i] 表示当剩下 i 个石子时,先手玩家是否能必胜。

14.3状态转移方程

对于每个 i
因此,状态转移方程为:
dp[i] = !dp[i - k] for all k in range(1, i // 2 + 1)

14.4Java实现

以下是Java语言实现:
public class StoneGame {
    public boolean stoneGame(int n) {
        // 创建一个布尔数组 dp,大小为 n+1
        boolean[] dp = new boolean[n + 1];
        
        // 初始化 dp 数组
        dp[0] = false; // 没有石子时,先手必败
        dp[1] = true;  // 只有一个石子时,先手必胜
        
        // 填充 dp 数组
        for (int i = 2; i <= n; i++) {
            dp[i] = false; // 默认先手必败
            for (int k = 1; k <= i / 2; k++) {
                if (!dp[i - k]) {
                    dp[i] = true; // 找到一个可以使对手必败的情况
                    break;
                }
            }
        }
        
        return dp[n];
    }

    public static void main(String[] args) {
        StoneGame game = new StoneGame();
        int n = 4;
        System.out.println("For " + n + " stones, first player wins: " + game.stoneGame(n)); // 输出: true
    }
}

14.5解释

  1. 初始化:我们创建一个布尔数组 dp,大小为 n + 1dp[i] 表示当剩下 i 个石子时,先手玩家是否能必胜。
  2. 基本情况dp[0]false(没有石子时先手必败),dp[1]true(只有一个石子时先手必胜)。
  3. 填充 dp 数组:对于每个 i,我们检查所有可能的取石子数量 k(从1i/2),如果存在一种情况使得对手处于必败状态(即 dp[i - k]false),则 dp[i]true
  4. 返回结果:最终结果存储在 dp[n] 中,表示当剩下 n 个石子时,先手玩家是否能必胜。
通过这种方式,我们可以高效地判断先手玩家是否有必胜策略。

15使用Java语言,实现最大矩形面积问题(在一个由01组成的二维矩阵中,求最大矩形面积)。

最大矩形面积问题是一个经典的动态规划问题。在一个由01组成的二维矩阵中,求出只包含1的最大矩形的面积。

15.1思路

我们可以将这个问题转化为多个“直方图的最大矩形面积”问题。具体步骤如下:
  1. 对于每一行,计算以该行为底的直方图中每个柱子的高度。
  2. 使用单调栈的方法来计算每行对应的直方图中的最大矩形面积。
  3. 最后,取所有行的最大矩形面积中的最大值。

15.2Java实现

以下是Java语言实现:
import java.util.Stack;

public class MaximalRectangle {
    public int maximalRectangle(char[][] matrix) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return 0;
        }
        
        int maxArea = 0;
        int[] heights = new int[matrix[0].length];
        
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                // Update the height array
                if (matrix[i][j] == '1') {
                    heights[j] += 1;
                } else {
                    heights[j] = 0;
                }
            }
            // Calculate the maximum area for this row's histogram
            maxArea = Math.max(maxArea, largestRectangleArea(heights));
        }
        
        return maxArea;
    }
    
    private int largestRectangleArea(int[] heights) {
        Stack<Integer> stack = new Stack<>();
        stack.push(-1);
        int maxArea = 0;
        
        for (int i = 0; i < heights.length; i++) {
            while (stack.peek() != -1 && heights[stack.peek()] >= heights[i]) {
                int height = heights[stack.pop()];
                int width = i - stack.peek() - 1;
                maxArea = Math.max(maxArea, height * width);
            }
            stack.push(i);
        }
        
        while (stack.peek() != -1) {
            int height = heights[stack.pop()];
            int width = heights.length - stack.peek() - 1;
            maxArea = Math.max(maxArea, height * width);
        }
        
        return maxArea;
    }

    public static void main(String[] args) {
        MaximalRectangle solution = new MaximalRectangle();
        char[][] matrix = {
            {'1', '0', '1', '0', '0'},
            {'1', '0', '1', '1', '1'},
            {'1', '1', '1', '1', '1'},
            {'1', '0', '0', '1', '0'}
        };
        int result = solution.maximalRectangle(matrix);
        System.out.println("Maximum rectangle area is: " + result); // 输出: 6
    }
}

15.3解释

  1. 初始化:我们首先检查输入矩阵是否为空或无效。如果是,则返回0。
  2. 高度数组:我们使用一个高度数组 heights 来记录每一列的高度。如果当前元素是1,则高度加1;否则,高度重置为0。
  3. 计算最大矩形面积:对于每一行,我们更新高度数组,然后调用 largestRectangleArea 方法来计算当前行的直方图中的最大矩形面积。
  4. 单调栈法:在 largestRectangleArea 方法中,我们使用单调栈来计算直方图中的最大矩形面积。通过维护一个递增栈,我们可以高效地找到每个柱子作为最小高度的矩形面积。
  5. 结果返回:最终返回所有行中的最大矩形面积。
通过这种方法,我们可以有效地解决最大矩形面积问题。

16使用Java语言,解决正则表达式匹配问题(使用动态规划判断一个字符串是否符合给定的正则表达式模式)。

要使用Java语言解决正则表达式匹配问题,我们可以利用动态规划的方法。这里我们考虑一个简化版本的正则表达式,它只包含字符、'.' 和 '*'。其中:

16.1步骤

  1. 定义状态: dp[i][j] 表示字符串的前 i 个字符与模式的前 j 个字符是否匹配。
  2. 初始化: dp[0][0] = true,因为空字符串与空模式是匹配的。对于所有 i > 0dp[i][0] = false,因为非空字符串不能与空模式匹配。
  3. 状态转移:
    • 如果当前字符匹配(s.charAt(i - 1) == p.charAt(j - 1) 或者 p.charAt(j - 1) == '.'),并且没有星号,那么 dp[i][j] = dp[i - 1][j - 1]
    • 如果存在星号,我们需要检查两种情况:
      • 星号代表前一个字符出现0次,即 dp[i][j] = dp[i][j - 2]
      • 星号代表前一个字符出现至少一次,且当前字符匹配,即 dp[i][j] = dp[i - 1][j]
  4. 返回结果: dp[s.length()][p.length()] 表示整个字符串和模式是否匹配。

16.2Java实现

public class RegexMatcher {
    public boolean isMatch(String s, String p) {
        int m = s.length();
        int n = p.length();
        boolean[][] dp = new boolean[m + 1][n + 1];
        dp[0][0] = true;

        for (int j = 1; j <= n; j++) {
            if (p.charAt(j - 1) == '*') {
                dp[0][j] = dp[0][j - 2];
            }
        }

        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (p.charAt(j - 1) == '.' || p.charAt(j - 1) == s.charAt(i - 1)) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else if (p.charAt(j - 1) == '*') {
                    dp[i][j] = dp[i][j - 2]; // Star represents zero occurrence of the preceding element
                    if (p.charAt(j - 2) == '.' || p.charAt(j - 2) == s.charAt(i - 1)) {
                        dp[i][j] = dp[i][j] || dp[i - 1][j]; // Star represents one or more occurrences
                    }
                }
            }
        }

        return dp[m][n];
    }

    public static void main(String[] args) {
        RegexMatcher matcher = new RegexMatcher();
        String s = "aab";
        String p = "c*a*b";
        System.out.println("Is match: " + matcher.isMatch(s, p)); // Should output true
    }
}
这段代码实现了一个简单的正则表达式匹配器,支持 .*。通过动态规划方法,我们能够高效地判断字符串是否符合给定的模式。

17使用Java语言,实现最长递增子序列(LIS)问题。

最长递增子序列(LIS)问题是一个经典的动态规划问题。给定一个整数数组,找到其中最长的严格递增子序列的长度。
以下是使用Java语言实现最长递增子序列问题的代码:
public class LongestIncreasingSubsequence {
    public int lengthOfLIS(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }

        // dp[i] 表示以 nums[i] 结尾的最长递增子序列的长度
        int[] dp = new int[nums.length];
        int maxLength = 1; // 初始化最大长度为1,因为每个元素自身可以作为一个子序列

        for (int i = 0; i < nums.length; i++) {
            dp[i] = 1; // 每个元素自身至少可以构成长度为1的子序列
            for (int j = 0; j < i; j++) {
                if (nums[j] < nums[i]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            maxLength = Math.max(maxLength, dp[i]);
        }

        return maxLength;
    }

    public static void main(String[] args) {
        LongestIncreasingSubsequence lis = new LongestIncreasingSubsequence();
        int[] nums = {10, 9, 2, 5, 3, 7, 101, 18};
        System.out.println("The length of the longest increasing subsequence is: " + lis.lengthOfLIS(nums));
    }
}

17.1解释

  1. 输入检查:首先检查输入数组是否为空或长度为零,如果是,则返回0。
  2. 初始化:创建一个 dp 数组,其中 dp[i] 表示以 nums[i] 结尾的最长递增子序列的长度。同时,初始化 maxLength1,因为每个元素自身可以作为一个子序列。
  3. 双重循环
    • 外层循环遍历数组中的每个元素 nums[i]
    • 内层循环遍历当前元素之前的所有元素 nums[j]。如果 nums[j] < nums[i],则更新 dp[i]dp[j] + 1dp[i] 的最大值。
  4. 更新最大长度:在每次更新 dp[i] 后,更新 maxLength 为当前 dp[i]maxLength 的最大值。
  5. 返回结果:最后返回 maxLength,即最长递增子序列的长度。

17.2示例输出

对于输入数组 {10, 9, 2, 5, 3, 7, 101, 18},程序将输出:
The length of the longest increasing subsequence is: 4
这是因为最长递增子序列是 {2, 3, 7, 101},其长度为4。

18使用Java语言,解决最大乘积子序列问题(在一个整数数组中找到乘积最大的连续子序列)。

最大乘积子序列问题是一个经典的动态规划问题。给定一个整数数组,找到其中乘积最大的连续子序列。
以下是使用Java语言实现最大乘积子序列问题的代码:
public class MaxProductSubarray {
    public int maxProduct(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }

        // 初始化最大值、最小值和结果
        int max = nums[0], min = nums[0], result = nums[0];

        for (int i = 1; i < nums.length; i++) {
            int tempMax = max; // 临时变量保存当前的最大值
            max = Math.max(Math.max(max * nums[i], min * nums[i]), nums[i]);
            min = Math.min(Math.min(tempMax * nums[i], min * nums[i]), nums[i]);
            result = Math.max(result, max);
        }

        return result;
    }

    public static void main(String[] args) {
        MaxProductSubarray solution = new MaxProductSubarray();
        int[] nums = {2, 3, -2, 4};
        System.out.println("The maximum product of a subarray is: " + solution.maxProduct(nums));
    }
}

18.1解释

  1. 输入检查:首先检查输入数组是否为空或长度为零,如果是,则返回0。
  2. 初始化:定义三个变量 maxminresult,分别表示当前最大乘积、当前最小乘积和全局最大乘积。初始时,它们都等于数组的第一个元素。
  3. 遍历数组:从第二个元素开始遍历数组:
    • 使用 tempMax 临时保存当前的 max 值,因为 max 在计算过程中会被更新。
    • 更新 max 为当前元素、当前元素与 max 的乘积、当前元素与 min 的乘积中的最大值。
    • 更新 min 为当前元素、当前元素与 tempMax 的乘积、当前元素与 min 的乘积中的最小值。
    • 更新 result 为当前 maxresult 的最大值。
  4. 返回结果:最后返回 result,即最大乘积子序列的乘积。

18.2示例输出

对于输入数组 {2, 3, -2, 4},程序将输出:
The maximum product of a subarray is: 6
这是因为最大乘积子序列是 {2, 3},其乘积为6。

19使用Java语言,实现最小跳跃次数问题(给定一个有序数组,求最小的跳跃次数到达最后一个元素)。

最小跳跃次数问题是一个经典的动态规划问题。给定一个有序数组,求从第一个元素跳到最后一个元素的最小跳跃次数。
以下是使用Java语言实现最小跳跃次数问题的代码:
public class MinJumps {
    public int minJumps(int[] nums) {
        if (nums == null || nums.length == 0) {
            return -1; // 如果数组为空,返回-1表示无法跳跃
        }

        int n = nums.length;
        if (n == 1) {
            return 0; // 如果数组只有一个元素,不需要跳跃
        }

        // jumps[i] 表示到达第 i 个位置所需的最小跳跃次数
        int[] jumps = new int[n];
        jumps[0] = 0; // 起始位置的跳跃次数为0

        for (int i = 1; i < n; i++) {
            jumps[i] = Integer.MAX_VALUE; // 初始化为最大值
            for (int j = 0; j < i; j++) {
                if (j + nums[j] >= i && jumps[j] != Integer.MAX_VALUE) {
                    jumps[i] = Math.min(jumps[i], jumps[j] + 1);
                }
            }
        }

        return jumps[n - 1]; // 返回到达最后一个位置的最小跳跃次数
    }

    public static void main(String[] args) {
        MinJumps solution = new MinJumps();
        int[] nums = {2, 3, 1, 1, 4};
        System.out.println("The minimum number of jumps to reach the end is: " + solution.minJumps(nums));
    }
}

19.1解释

  1. 输入检查:首先检查输入数组是否为空或长度为零,如果是,则返回-1表示无法跳跃。
  2. 初始化:定义一个 jumps 数组,其中 jumps[i] 表示到达第 i 个位置所需的最小跳跃次数。初始时,将 jumps[0] 设为0,因为起始位置不需要跳跃。
  3. 遍历数组:从第二个元素开始遍历数组:
    • 初始化 jumps[i] 为最大值 Integer.MAX_VALUE
    • 内层循环遍历当前元素之前的所有元素 j,如果从 j 可以跳到 i(即 j + nums[j] >= i),并且 jumps[j] 不是最大值,则更新 jumps[i]jumps[j] + 1 和当前 jumps[i] 的最小值。
  4. 返回结果:最后返回 jumps[n - 1],即到达最后一个位置的最小跳跃次数。

19.2示例输出

对于输入数组 {2, 3, 1, 1, 4},程序将输出:
The minimum number of jumps to reach the end is: 2
这是因为最少需要两次跳跃才能到达最后一个元素。第一次从索引0跳到索引1,第二次从索引1跳到索引4。

20使用Java语言,解决最大正方形子矩阵问题(在一个由01组成的二维矩阵中,求最大的全为1的正方形子矩阵的边长)。

最大正方形子矩阵问题是一个经典的动态规划问题。给定一个由01组成的二维矩阵,求最大的全为1的正方形子矩阵的边长。
以下是使用Java语言实现最大正方形子矩阵问题的代码:
public class MaximalSquare {
    public int maximalSquare(char[][] matrix) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return 0;
        }

        int rows = matrix.length, cols = matrix[0].length;
        int[][] dp = new int[rows + 1][cols + 1];
        int maxSide = 0;

        for (int i = 1; i <= rows; i++) {
            for (int j = 1; j <= cols; j++) {
                if (matrix[i - 1][j - 1] == '1') {
                    dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
                    maxSide = Math.max(maxSide, dp[i][j]);
                }
            }
        }

        return maxSide * maxSide; // 返回最大正方形的面积
    }

    public static void main(String[] args) {
        MaximalSquare solution = new MaximalSquare();
        char[][] matrix = {
            {'1', '0', '1', '0', '0'},
            {'1', '0', '1', '1', '1'},
            {'1', '1', '1', '1', '1'},
            {'1', '0', '0', '1', '0'}
        };
        System.out.println("The area of the largest square containing only 1s is: " + solution.maximalSquare(matrix));
    }
}

20.1解释

  1. 输入检查:首先检查输入矩阵是否为空或长度为零,如果是,则返回0。
  2. 初始化:定义一个 dp 数组,其中 dp[i][j] 表示以 (i-1, j-1) 为右下角的最大正方形的边长。同时定义一个变量 maxSide 来记录当前找到的最大正方形的边长。
  3. 遍历矩阵:从第二行和第二列开始遍历矩阵:
    • 如果当前元素是 '1',则更新 dp[i][j]dp[i-1][j], dp[i][j-1], dp[i-1][j-1] 中的最小值加1。
    • 更新 maxSide 为当前 dp[i][j]maxSide 的最大值。
  4. 返回结果:最后返回 maxSide * maxSide,即最大正方形的面积。

20.2示例输出

对于输入矩阵:
{
    {'1', '0', '1', '0', '0'},
    {'1', '0', '1', '1', '1'},
    {'1', '1', '1', '1', '1'},
    {'1', '0', '0', '1', '0'}
}
程序将输出:
The area of the largest square containing only 1s is: 4
这是因为最大的全为1的正方形子矩阵的边长为2,其面积为4。

21使用Java语言,实现最长回文子串问题。

最长回文子串问题是一个经典的动态规划问题。给定一个字符串,找到其中最长的回文子串。
以下是使用Java语言实现最长回文子串问题的代码:
public class LongestPalindromicSubstring {
    public String longestPalindrome(String s) {
        if (s == null || s.length() < 1) return "";
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
            int len1 = expandAroundCenter(s, i, i); // 奇数长度的回文
            int len2 = expandAroundCenter(s, i, i + 1); // 偶数长度的回文
            int len = Math.max(len1, len2);
            if (len > end - start) {
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1);
    }

    private int expandAroundCenter(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            left--;
            right++;
        }
        return right - left - 1;
    }

    public static void main(String[] args) {
        LongestPalindromicSubstring solution = new LongestPalindromicSubstring();
        String s = "babad";
        System.out.println("The longest palindromic substring is: " + solution.longestPalindrome(s));
    }
}

21.1解释

  1. 输入检查:首先检查输入字符串是否为空或长度小于1,如果是,则返回空字符串。
  2. 初始化:定义两个变量 startend 来记录最长回文子串的起始和结束位置。
  3. 遍历字符串:从字符串的每一个字符开始,尝试以该字符为中心扩展回文子串:
    • 调用 expandAroundCenter 方法计算以当前字符为中心的奇数长度回文子串的长度。
    • 调用 expandAroundCenter 方法计算以当前字符和下一个字符为中心的偶数长度回文子串的长度。
    • 取两者的最大值,并更新 startend
  4. 返回结果:最后返回从 startend 的子串,即为最长回文子串。
  5. 辅助方法expandAroundCenter 方法用于从中心向两边扩展,计算回文子串的长度。

21.2示例输出

对于输入字符串 "babad",程序将输出:
The longest palindromic substring is: "bab" or "aba"
这是因为在字符串 "babad" 中,最长的回文子串是 "bab""aba"

22使用Java语言,解决最大连续1的个数问题(在一个二进制数组中找到最长的连续1的子数组长度)。

最大连续1的个数问题是一个经典的数组遍历问题。给定一个二进制数组,找到其中最长的连续1的子数组长度。
以下是使用Java语言实现最大连续1的个数问题的代码:
public class MaxConsecutiveOnes {
    public int findMaxConsecutiveOnes(int[] nums) {
        int maxCount = 0;
        int currentCount = 0;

        for (int num : nums) {
            if (num == 1) {
                currentCount++;
                maxCount = Math.max(maxCount, currentCount);
            } else {
                currentCount = 0;
            }
        }

        return maxCount;
    }

    public static void main(String[] args) {
        MaxConsecutiveOnes solution = new MaxConsecutiveOnes();
        int[] nums = {1, 1, 0, 1, 1, 1};
        System.out.println("The maximum number of consecutive 1s is: " + solution.findMaxConsecutiveOnes(nums));
    }
}

22.1解释

  1. 初始化:定义两个变量 maxCountcurrentCount,分别用于记录当前找到的最大连续1的长度和当前连续1的长度。
  2. 遍历数组:使用增强型for循环遍历数组中的每个元素:
    • 如果当前元素是1,则将 currentCount1,并更新 maxCountmaxCountcurrentCount 中的较大值。
    • 如果当前元素不是1,则将 currentCount 重置为0。
  3. 返回结果:遍历结束后,返回 maxCount,即最长连续1的子数组长度。

22.2示例输出

对于输入数组 {1, 1, 0, 1, 1, 1},程序将输出:
The maximum number of consecutive 1s is: 3
这是因为在数组中,最长的连续1的子数组长度为3。

23使用Java语言,实现最大乘积子数组问题(在一个整数数组中找到乘积最大的连续子数组)。

最大乘积子数组问题是一个经典的动态规划问题。给定一个整数数组,找到其中乘积最大的连续子数组。
以下是使用Java语言实现最大乘积子数组问题的代码:
public class MaximumProductSubarray {
    public int maxProduct(int[] nums) {
        if (nums == null || nums.length == 0) return 0;

        int max = nums[0], min = nums[0], result = nums[0];

        for (int i = 1; i < nums.length; i++) {
            int temp = max;
            max = Math.max(Math.max(max * nums[i], min * nums[i]), nums[i]);
            min = Math.min(Math.min(temp * nums[i], min * nums[i]), nums[i]);
            result = Math.max(result, max);
        }

        return result;
    }

    public static void main(String[] args) {
        MaximumProductSubarray solution = new MaximumProductSubarray();
        int[] nums = {2, 3, -2, 4};
        System.out.println("The maximum product of a subarray is: " + solution.maxProduct(nums));
    }
}

23.1解释

  1. 初始化:定义三个变量 max, minresult,分别用于记录当前的最大值、最小值和全局最大值。初始时,它们都等于数组的第一个元素。
  2. 遍历数组:从第二个元素开始遍历数组:
    • 使用临时变量 temp 保存当前的 max 值。
    • 更新 max 为当前元素、当前元素与 max 的乘积、当前元素与 min 的乘积中的最大值。
    • 更新 min 为当前元素、当前元素与 temp(即之前的 max)的乘积、当前元素与 min 的乘积中的最小值。
    • 更新 resultresultmax 中的较大值。
  3. 返回结果:遍历结束后,返回 result,即乘积最大的连续子数组的乘积。

23.2示例输出

对于输入数组 {2, 3, -2, 4},程序将输出:
The maximum product of a subarray is: 6
这是因为在数组中,乘积最大的连续子数组是 [2, 3],其乘积为6。

24使用Java语言,解决最大和子数组问题(在一个整数数组中找到和最大的连续子数组)。

最大和子数组问题是一个经典的动态规划问题。给定一个整数数组,找到其中和最大的连续子数组。
以下是使用Java语言实现最大和子数组问题的代码:
public class MaximumSubarray {
    public int maxSubArray(int[] nums) {
        if (nums == null || nums.length == 0) return 0;

        int currentSum = nums[0];
        int maxSum = nums[0];

        for (int i = 1; i < nums.length; i++) {
            currentSum = Math.max(nums[i], currentSum + nums[i]);
            maxSum = Math.max(maxSum, currentSum);
        }

        return maxSum;
    }

    public static void main(String[] args) {
        MaximumSubarray solution = new MaximumSubarray();
        int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
        System.out.println("The maximum sum of a subarray is: " + solution.maxSubArray(nums));
    }
}

24.1解释

  1. 初始化:定义两个变量 currentSummaxSum,分别用于记录当前子数组的和以及全局最大子数组的和。初始时,它们都等于数组的第一个元素。
  2. 遍历数组:从第二个元素开始遍历数组:
    • 更新 currentSum 为当前元素和 currentSum + 当前元素 中的较大值。这一步决定了是否将当前元素加入现有的子数组中,还是重新开始一个新的子数组。
    • 更新 maxSummaxSumcurrentSum 中的较大值。这一步确保了 maxSum 始终是遍历到当前位置的最大子数组和。
  3. 返回结果:遍历结束后,返回 maxSum,即和最大的连续子数组的和。

24.2示例输出

对于输入数组 {-2, 1, -3, 4, -1, 2, 1, -5, 4},程序将输出:
The maximum sum of a subarray is: 6
这是因为在数组中,和最大的连续子数组是 [4, -1, 2, 1],其和为6。

25使用Java语言,解决最小删除操作使字符串回文的问题。

最小删除操作使字符串回文的问题可以通过动态规划来解决。给定一个字符串,找到将其转换为回文所需的最少删除次数。
以下是使用Java语言实现最小删除操作使字符串回文问题的代码:
public class MinDeletionsToPalindrome {
    public int minDeletionsToMakePalindrome(String s) {
        int n = s.length();
        int[][] dp = new int[n][n];

        for (int len = 2; len <= n; len++) {
            for (int i = 0; i <= n - len; i++) {
                int j = i + len - 1;
                if (s.charAt(i) == s.charAt(j)) {
                    dp[i][j] = dp[i + 1][j - 1];
                } else {
                    dp[i][j] = Math.min(dp[i + 1][j], dp[i][j - 1]) + 1;
                }
            }
        }

        return dp[0][n - 1];
    }

    public static void main(String[] args) {
        MinDeletionsToPalindrome solution = new MinDeletionsToPalindrome();
        String s = "abca";
        System.out.println("The minimum deletions to make the string a palindrome are: " + solution.minDeletionsToMakePalindrome(s));
    }
}

25.1解释

  1. 初始化:定义一个二维数组 dp,其中 dp[i][j] 表示将子串 s[i...j] 转换为回文所需的最少删除次数。
  2. 填充表格
    • 遍历所有可能的子串长度 len,从2开始到字符串的长度 n
    • 对于每个子串长度,遍历起始位置 i,计算结束位置 j = i + len - 1
    • 如果 s[i] 等于 s[j],则 dp[i][j] = dp[i + 1][j - 1],因为这两个字符已经匹配,不需要删除。
    • 如果 s[i] 不等于 s[j],则 dp[i][j] = Math.min(dp[i + 1][j], dp[i][j - 1]) + 1,表示要么删除 s[i],要么删除 s[j],取两者中的较小值再加1。
  3. 返回结果:最终 dp[0][n - 1] 就是将整个字符串转换为回文所需的最少删除次数。

25.2示例输出

对于输入字符串 "abca",程序将输出:
The minimum deletions to make the string a palindrome are: 1
这是因为只需要删除一个字符(例如删除 'b'),就可以将字符串 "abca" 转换为回文 "aca"

26使用Java语言,实现最大乘积子序列问题(在一个整数数组中找到乘积最大的连续子数组)。

最大乘积子序列问题是一个经典的动态规划问题。给定一个整数数组,找到其中乘积最大的连续子数组。
以下是使用Java语言实现最大乘积子序列问题的代码:
public class MaximumProductSubarray {
    public int maxProduct(int[] nums) {
        if (nums == null || nums.length == 0) return 0;

        int max = nums[0], min = nums[0], result = nums[0];

        for (int i = 1; i < nums.length; i++) {
            int temp = max;
            max = Math.max(Math.max(max * nums[i], min * nums[i]), nums[i]);
            min = Math.min(Math.min(temp * nums[i], min * nums[i]), nums[i]);
            result = Math.max(result, max);
        }

        return result;
    }

    public static void main(String[] args) {
        MaximumProductSubarray solution = new MaximumProductSubarray();
        int[] nums = {2, 3, -2, 4};
        System.out.println("The maximum product of a subarray is: " + solution.maxProduct(nums));
    }
}

26.1解释

  1. 初始化:定义三个变量 max, minresult,分别用于记录当前的最大值、最小值和全局最大值。初始时,它们都等于数组的第一个元素。
  2. 遍历数组:从第二个元素开始遍历数组:
    • 使用临时变量 temp 保存当前的 max 值。
    • 更新 max 为当前元素、当前元素与 max 的乘积、当前元素与 min 的乘积中的最大值。
    • 更新 min 为当前元素、当前元素与 temp(即之前的 max)的乘积、当前元素与 min 的乘积中的最小值。
    • 更新 resultresultmax 中的较大值。
  3. 返回结果:遍历结束后,返回 result,即乘积最大的连续子数组的乘积。

26.2示例输出

对于输入数组 {2, 3, -2, 4},程序将输出:
The maximum product of a subarray is: 6
这是因为在数组中,乘积最大的连续子数组是 [2, 3],其乘积为6。

27使用Java语言,解决最大和子数组问题(在一个整数数组中找到和最大的连续子数组)。

最大和子数组问题是一个经典的动态规划问题。给定一个整数数组,找到其中和最大的连续子数组。
以下是使用Java语言实现最大和子数组问题的代码:
public class MaximumSubarray {
    public int maxSubArray(int[] nums) {
        if (nums == null || nums.length == 0) return 0;

        int currentSum = nums[0];
        int maxSum = nums[0];

        for (int i = 1; i < nums.length; i++) {
            currentSum = Math.max(nums[i], currentSum + nums[i]);
            maxSum = Math.max(maxSum, currentSum);
        }

        return maxSum;
    }

    public static void main(String[] args) {
        MaximumSubarray solution = new MaximumSubarray();
        int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
        System.out.println("The maximum sum of a subarray is: " + solution.maxSubArray(nums));
    }
}

27.1解释

  1. 初始化:定义两个变量 currentSummaxSum,分别用于记录当前子数组的和以及全局最大子数组的和。初始时,它们都等于数组的第一个元素。
  2. 遍历数组:从第二个元素开始遍历数组:
    • 更新 currentSum 为当前元素和 currentSum + 当前元素 中的较大值。这一步决定了是否将当前元素加入现有的子数组中,还是重新开始一个新的子数组。
    • 更新 maxSummaxSumcurrentSum 中的较大值。这一步确保了 maxSum 始终是遍历到当前位置的最大子数组和。
  3. 返回结果:遍历结束后,返回 maxSum,即和最大的连续子数组的和。

27.2示例输出

对于输入数组 {-2, 1, -3, 4, -1, 2, 1, -5, 4},程序将输出:
The maximum sum of a subarray is: 6
这是因为在数组中,和最大的连续子数组是 [4, -1, 2, 1],其和为6。

28使用Java语言,解决最小删除操作使字符串回文的问题。

最小删除操作使字符串回文的问题可以通过动态规划来解决。给定一个字符串,找到将其转换为回文所需的最少删除次数。
以下是使用Java语言实现最小删除操作使字符串回文问题的代码:
public class MinDeletionsToPalindrome {
    public int minDeletionsToMakePalindrome(String s) {
        int n = s.length();
        int[][] dp = new int[n][n];

        for (int len = 2; len <= n; len++) {
            for (int i = 0; i <= n - len; i++) {
                int j = i + len - 1;
                if (s.charAt(i) == s.charAt(j)) {
                    dp[i][j] = dp[i + 1][j - 1];
                } else {
                    dp[i][j] = Math.min(dp[i + 1][j], dp[i][j - 1]) + 1;
                }
            }
        }

        return dp[0][n - 1];
    }

    public static void main(String[] args) {
        MinDeletionsToPalindrome solution = new MinDeletionsToPalindrome();
        String s = "abca";
        System.out.println("The minimum deletions to make the string a palindrome are: " + solution.minDeletionsToMakePalindrome(s));
    }
}

28.1解释

  1. 初始化:定义一个二维数组 dp,其中 dp[i][j] 表示将子串 s[i...j] 转换为回文所需的最少删除次数。
  2. 填充表格
    • 遍历所有可能的子串长度 len,从2开始到字符串的长度 n
    • 对于每个子串长度,遍历起始位置 i,计算结束位置 j = i + len - 1
    • 如果 s[i] 等于 s[j],则 dp[i][j] = dp[i + 1][j - 1],因为这两个字符已经匹配,不需要删除。
    • 如果 s[i] 不等于 s[j],则 dp[i][j] = Math.min(dp[i + 1][j], dp[i][j - 1]) + 1,表示要么删除 s[i],要么删除 s[j],取两者中的较小值再加1。
  3. 返回结果:最终 dp[0][n - 1] 就是将整个字符串转换为回文所需的最少删除次数。

28.2示例输出

对于输入字符串 "abca",程序将输出:
The minimum deletions to make the string a palindrome are: 1
这是因为只需要删除一个字符(例如删除 'b'),就可以将字符串 "abca" 转换为回文 "aca"