算法题分享

237 阅读4分钟

算法题分享

几个算法题,分享下解题思路和答案。

题目1 最小花费爬楼梯

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

输入:cost = [10,15,20] 输出:15 解释:你将从下标为 1 的台阶开始。

  • 支付 15 ,向上爬两个台阶,到达楼梯顶部。 总花费为 15 。

递归版本:

public int minCostClimbingStairs3(int[] cost) {
    if (cost.length == 2) {
        return Math.min(cost[0], cost[1]);
    }
    int ans1 = process(cost, 0);
    int ans2 = process(cost, 1);
    return Math.min(ans1, ans2);
}
​
/**
 * 从i到最后的最小代价
 *
 * @param cost
 * @param i
 * @return
 */
private int process(int[] cost, int i) {
    if (i >= cost.length) {
        return 0;
    }
​
    int ans;
    if (i == cost.length - 1) {
        ans = cost[i];
    } else {
        int p1 = cost[i] + process(cost, i + 1);
        int p2 = cost[i] + process(cost, i + 2);
        ans = Math.min(p1, p2);
    }
    return ans;
}

动态规划:

public int minCostClimbingStairs(int[] cost) {
    if (cost.length == 2) {
        return Math.min(cost[0], cost[1]);
    }
    int[] dp = new int[cost.length];
    process(cost, 0, dp);
    return Math.min(dp[0], dp[1]);
}
​
/**
 * 从i到最后的最小代价
 *
 * @param cost
 * @param i
 * @return
 */
private int process(int[] cost, int i, int dp[]) {
    if (i >= cost.length) {
        return 0;
    }
​
    if (dp[i] != 0) {
        return dp[i];
    }
    int ans;
    if (i == cost.length - 1) {
        ans = cost[i];
    } else {
        int p1 = cost[i] + process(cost, i + 1, dp);
        int p2 = cost[i] + process(cost, i + 2, dp);
        ans = Math.min(p1, p2);
    }
    dp[i] = ans;
    return ans;
}

动态规划2:

public int minCostClimbingStairs2(int[] cost) {
    if (cost.length == 2) {
        return Math.min(cost[0], cost[1]);
    }
    int N = cost.length;
    int dp[] = new int[N];
    dp[N - 1] = cost[N - 1];
    dp[N - 2] = cost[N - 2];
    for (int i = N - 3; i >= 0; i--) {
        dp[i] = Math.min(cost[i] + dp[i + 1], cost[i] + dp[i + 2]);
    }
    return Math.min(dp[0], dp[1]);
}

对数器:

public static int[] createRandomArray(int start, int end, int count) {
    int[] ret = new int[count];
    Random random = new Random();
    for (int i = 0; i < count; i++) {
        ret[i] = random.nextInt(end) + start;
    }
    return ret;
}
​
    public static void main(String[] args) {
//        int[] cost = {1, 2, 2, 0};
//        int[] cost = {1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100};
        int start = 1;
        int end = 1000;
        int count = 1000000;
        for (int i = 0; i < count; i++) {
            int[] cost = createRandomArray(start, end, 10);
            int ans = new Solution().minCostClimbingStairs(cost);
            int ans2 = new Solution().minCostClimbingStairs2(cost);
            int ans3 = new Solution().minCostClimbingStairs3(cost);
            if (ans != ans2 || ans != ans3) {
                System.out.println("oops!");
            }
        }
        System.out.println("finish");
    }

leetCode原题地址:746. 使用最小花费爬楼梯 - 力扣(LeetCode)

题目2 最小路径之和

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:一个机器人每次只能向下或者向右移动一步。

img

递归版本:

    public int minPathSum(int[][] grid) {
        return process(grid, 0, 0);
    }
​
    int process(int[][] grid, int x, int y) {
        int n = grid.length;
        int m = grid[0].length;
        if (x == n - 1 && y == m - 1) {
            return grid[x][y];
        }
        int res = 0;
        if (x == n - 1) {//下边到头只能往右走
            res = grid[x][y] + process(grid, x, y + 1);
            return res;
        } else if (y == m - 1) {//只能往下走
            res = grid[x][y] + process(grid, x + 1, y);
            return res;
        }
        res = grid[x][y] + (Math.min(process(grid, x + 1, y), process(grid, x, y + 1)));
        return res;
    }
​

动态规划:

 public int minPathSum2(int[][] grid) {
        return process2(grid, 0, 0,new HashMap<>());
    }
    
int process2(int[][] grid, int x, int y, HashMap<String, Integer> map) {
    int n = grid.length;
    int m = grid[0].length;
    if (map.containsKey(x + "_" + y)) {
        return map.get(x + "_" + y);
    }
    if (x == n - 1 && y == m - 1) {
        map.put(x + "_" + y, grid[x][y]);
        return grid[x][y];
    }
    int res = 0;
    if (x == n - 1) {//下边到头只能往右走
        res = grid[x][y] + process2(grid, x, y + 1, map);
        map.put(x + "_" + y, res);
        return res;
    } else if (y == m - 1) {//只能往下走
        res = grid[x][y] + process2(grid, x + 1, y, map);
        map.put(x + "_" + y, res);
        return res;
    }
    res = grid[x][y] + (Math.min(process2(grid, x + 1, y, map), process2(grid, x, y + 1, map)));
    map.put(x + "_" + y, res);
    return res;
}

动态规划2:

  public int minPathSum(int[][] grid) {
        int N = grid.length;
        int M = grid[0].length;
        int dp[][] = new int[N][M];
        //dp[x-1][y-1]= grid[x][y];
        for (int i = N - 1; i >= 0; i--) {
            for (int j = M - 1; j >= 0; j--) {
                if (i == N - 1 && j == M - 1) {//到了右下角
                    dp[i][j] = grid[i][j];
                } else if (i == N - 1) {//只能往右走
                    dp[i][j] = grid[i][j] + dp[i][j + 1];
                } else if (j == M - 1) {//只能往下走
                    dp[i][j] = grid[i][j] + dp[i + 1][j];
                } else {//可以左右也可以走下
                    dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]);
                }
            }
        }
        return dp[0][0];
    }

leetCode原题地址:剑指 Offer II 099. 最小路径之和

题目3 最长的有小括号

给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

示例 1:

输入:s = "(()" 输出:2 解释:最长有效括号子串是 "()" 示例 2:

输入:s = ")()())" 输出:4 解释:最长有效括号子串是 "()()" 示例 3:

输入:s = "" 输出:0

提示:

0 <= s.length <= 3 * 104 s[i] 为 '(' 或 ')'

递归版本:

 private int process(char[] str, int i) {
        if (i <= 0) return 0;
        int cur = 0;
        if (str[i] == ')') {//左边都是结算过的
            int left = process(str, i - 1);
            int pre = i - left - 1;
            if (pre >= 0 && str[pre] == '(') {
                cur = left + 2 + (pre > 0 ? process(str, pre - 1) : 0);
            }
        } else {
            process(str, i - 1);
        }
        ans = Math.max(ans, cur);
        return cur;
}

动态规划:

int ans = Integer.MIN_VALUE;

private int process(char[] str, int i, int[] dp) {
        if (i <= 0) return 0;
        if (dp[i] != 0) return dp[i];
        int cur = 0;
        if (str[i] == ')') {//左边都是结算过的
            int left = process(str, i - 1,dp);
            int pre = i - left - 1;
            if (pre >= 0 && str[pre] == '(') {
                cur = left + 2 + (pre > 0 ? process(str, pre - 1,dp) : 0);
                dp[i] = cur;
            }
        } else {
            process(str, i - 1, dp);
        }
        ans = Math.max(ans, cur);
        return cur;
  }

动态规划2:

  int longestValidParentheses(String s) {
        if (s == null || s.length() < 2) return 0;
        char[] str = s.toCharArray();
        //dp[i] 字串必须以i为结尾往左最多有多少的有效区域
        int[] dp = new int[str.length];
        int pre, ans = 0;
        for (int i = 1; i < str.length; i++) {//0位置肯定是0
            if (str[i] == ')') {
                pre = i - dp[i - 1] - 1;//与str[i] 配对的左括号的位置
                if (pre >= 0 && str[pre] == '(') {
                    dp[i] = dp[i - 1] + 2 + (pre > 0 ? dp[pre - 1] : 0);
                }
            }
            ans = Math.max(ans, dp[i]);
        }
        return ans;
    }

LeetCode原题:最长有效括号

题目4 打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入:[1,2,3,1] 输出:4 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。 示例 2:

输入:[2,7,9,3,1] 输出:12 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 偷窃到的最高金额 = 2 + 9 + 1 = 12 。

提示:

1 <= nums.length <= 100 0 <= nums[i] <= 400

递归: 会超时

    public int rob(int[] nums) {
        return process(nums, 0);
    }

    private int process(int[] nums, int i) {
        if (i >= nums.length) return 0;
        //不从当前位置偷
        int p1 = process(nums, i + 1);
        //从当前位置偷
        int p2 = nums[i] + process(nums, i + 2);
        return Math.max(p1, p2);
    }

动态规划:

 private int process(int[] nums, int i, int[] dp) {
        if (i >= nums.length) return 0;
        if (dp[i] != -1) {
            return dp[i];
        }
        //不从当前位置偷
        int p1 = process(nums, i + 1, dp);
        //从当前位置偷
        int p2 = nums[i] + process(nums, i + 2, dp);
        dp[i] = Math.max(p1, p2);
        return dp[i];
    }

动态规划2:

public int rob2(int[] nums) {
    int N = nums.length;
    int dp[] = new int[N];
    dp[N - 1] = nums[N - 1];
    dp[N - 2] = Math.max(nums[N - 1], nums[N - 2]);
    for (int i = N - 3; i >= 0; i--) {
        dp[i] = Math.max(dp[i + 1], nums[i] + dp[i + 2]);
    }
    return dp[0];
}

LeetCode原题地址:打家劫舍