DP

10. 正则表达式匹配

/**
 * * 匹配零个或者多个前面的元素
 * . 匹配任意单个元素
 */
class Solution {
    public boolean isMatch(String s, String p) {
        if (s == null || p == null) return false;
        boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
        dp[0][0] = true;
        for (int i = 1; i <= p.length(); i++) {
            if (p.charAt(i - 1) == '*')
                // 初始化最左边列, 根据题意 * 不可能是 p的第一个元素,所以这里的 i-2不会越界
                dp[0][i] = dp[0][i - 2];
        }
        // 一列一列的比较,从上到下,这里 pi si 表示的是字符串的个数
        for (int si = 1; si <= s.length(); si++) {
            for (int pi = 1; pi <= s.length(); pi++) {
                // pi字符是 . 或者 pi字符和 si字符一样
                if (p.charAt(pi - 1) == '.' || p.charAt(pi - 1) == s.charAt(si - 1))
                    // dp[si][pi]是否匹配,取决于 pi和 si之前的字符是否匹配
                    dp[si][pi] = dp[si - 1][pi - 1];
                else if (p.charAt(pi - 1) == '*') { // pi个字符是 *
                    // 如果 * 的前面是 . 或者等于 si字符
                    if (p.charAt(pi - 2) == '.' || p.charAt(pi - 2) == s.charAt(si - 1))
                        // 则当前位置是否匹配取决于上面两种情况下任意一种匹配
                        dp[si][pi] = (dp[si][pi - 2] || dp[si - 1][pi]);
                    else dp[si][pi] = dp[si][pi - 2]; // pi的前一个字符不是 . 也不和si相等
                }
            }
        }
        return dp[s.length()][p.length()];
    }
}

在这里插入图片描述
在这里插入图片描述

64. 最小路径和

在这里插入图片描述

class Solution {
    public static void main(String[] args) {
        int[][] grid = {{1, 3, 1}, {1, 5, 1}, {4, 2, 1}};
        System.out.println(minPathSum2(grid));
    }

    /**
     * O(mn)
     * O(mn)
     * @param grid
     * @return
     */
    public static int minPathSum(int[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) return 0;
        int m = grid.length; // m行
        int n = grid[0].length; // n列
        int[][] dp = new int[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (i == 0 && j == 0)
                    dp[0][0] = grid[0][0];
                else if (i == 0)
                    dp[i][j] = dp[i][j - 1] + grid[i][j];
                else if (j == 0)
                    dp[i][j] = dp[i - 1][j] + grid[i][j];
                else
                    dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
            }
        }
        return dp[m - 1][n - 1];
    }

    /**
     * O(mn)
     * O(n)
     * @param grid
     * @return
     */
    public static int minPathSum2(int[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) return 0;
        int m = grid.length; // m行
        int n = grid[0].length; // n列
        int[] dp = new int[n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (i == 0 && j == 0) // 第一个数
                    dp[j] = grid[0][0];
                else if (i == 0) // 第一行,左一个dp加上当前数
                    dp[j] = dp[j - 1] + grid[i][j];
                else if (j == 0) // 第一列,上一个dp加上当前数
                    dp[j] = dp[j] + grid[i][j];
                else // 上一个 和 左一个的较小值 加上当前数
                    dp[j] = Math.min(dp[j], dp[j - 1]) + grid[i][j];
            }
        }
        return dp[n - 1];
    }

    /**
     * O(mn)
     * O(1)
     * @param grid
     * @return
     */
    public static int minPathSum3(int[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) return 0;
        int m = grid.length; // m行
        int n = grid[0].length; // n列
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (i == 0 && j == 0)
                    grid[0][0] = grid[0][0];
                else if (i == 0)
                    grid[i][j] = grid[i][j - 1] + grid[i][j];
                else if (j == 0)
                    grid[i][j] = grid[i - 1][j] + grid[i][j];
                else
                    grid[i][j] = Math.min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j];
            }
        }
        return grid[m - 1][n - 1];
    }
}

70. 爬楼梯

在这里插入图片描述

class Solution {
    public int climbStairs(int n) {
        if (n == 1)  return 1;
        if (n == 2) return 2;
        // 一共有n个数,又因为dp[1]=1
        // 所以从1开始使用的,要返回dp[n],所以长度为n+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 int climbStairs2(int n) {
        if (n == 1)  return 1;
        if (n == 2) return 2;
        // 对于第n层来说,只需要知道到达第 n-1,和第 n-2层一共有多少种走法
        // 所以只需要用两个值来记录这两个数即可
        int prev = 1;
        int cur = 2;
        for (int i = 3; i <= n; i++) {
            // 转移方程
            cur = cur + prev;
            prev = cur - prev;
        }
        return cur;
    }
}

在这里插入图片描述

120. 三角形的最小路径和

方法一 (自底向上)
class Solution {
    /**
     * dp: 自底向上
     * https://leetcode-cn.com/problems/triangle/solution/javadong-tai-gui-hua-si-lu-yi-ji-dai-ma-shi-xian-b/
     * @param triangle
     * @return
     */
    public static int minimumTotal(List<List<Integer>> triangle) {
        // 特判
        if (triangle == null || triangle.size() == 0) {
            return 0;
        }
        // 加1可以不用初始化最后一行, 因为多加了一行在最后面
        // 根据题意,每一行的行数和每一行数据个数相同,按照最大值创建dp数组
        int[][] dp = new int[triangle.size() + 1][triangle.size() + 1];
        for (int i = triangle.size() - 1; i >= 0; i--) {
            List<Integer> rows = triangle.get(i);
            for (int j = 0; j < rows.size(); j++) {
                // 当前位置[i][j]到 bottom的最小路径和 = 左边路径和 与 右边路径和的较小值
                dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + rows.get(j);
            }
        }
        return dp[0][0];
    }
}

在这里插入图片描述
空间优化

/**
 * 空间优化  O(n) n为三角形总行数(其实是最底层那行,数据的个数两者相等)
 * @param triangle
 * @return
 */
public static int minimumTotalOptimize(List<List<Integer>> triangle) {
    // 特判
    if (triangle == null || triangle.size() == 0) return 0;
    int row  = triangle.size();
    // 在最后一行的时候,计算dp[3] = Math.min(dp[3], dp[3+1]) + ...
    int[] dp = new int[row + 1]; // 创建的dp[]最大程度为: size+1
    for (int i = row - 1; i >= 0 ; i--) { // 从最后一行开始遍历
        for (int j = 0; j <= i; j++) {
            dp[j] = Math.min(dp[j], dp[j+1]) + triangle.get(i).get(j);
        }
    }
    return dp[0];
}

在这里插入图片描述

方法二 (自顶向下)
class Solution5 {
    public static void main(String[] args) {
    /**
     * dp: 自顶向下
     * https://leetcode-cn.com/problems/triangle/solution/javadong-tai-gui-hua-si-lu-yi-ji-dai-ma-shi-xian-b/
     * @param triangle
     * @return
     */
    public static int minimumTotal(List<List<Integer>> triangle) {
        // 特判
        if (triangle == null || triangle.size() == 0) {
            return 0;
        }
        int row = triangle.size(); // 三角形行数
        int column = triangle.get(row - 1).size(); // 最后一行的列数
        // 记录包含 [i][j]元素的最小路径和
        int[][] dp = new int[row][column];
        // 初始化第一个
        dp[0][0] = triangle.get(0).get(0);
        int res = Integer.MAX_VALUE;
        // 遍历行,从第二行开始(row = 2)
        for (int i = 1; i < row; i++) {
            // 对每一行的元素进行推导,第几行就有几个元素
            for (int j = 0; j <= i; j++) {
                if (j == 0) { // 最左端特殊处理
                    dp[i][j] = dp[i - 1][j] + triangle.get(i).get(j);
                } else if (j == i) { // 最右端特殊处理
                    dp[i][j] = dp[i - 1][j - 1] + triangle.get(i).get(j);
                } else {
                    dp[i][j] = Math.min(dp[i - 1][j], dp[i - 1][j - 1]) + triangle.get(i).get(j);
                }
            }
        }

        // 遍历完之后,dp的最后一行保存了三角形最后一行每个数据的最小路径和,找出其中最小的
        for (int i = 0; i < column; i++) {
            res = Math.min(res, dp[row - 1][i]);
        }
        return res;
    }
}

在这里插入图片描述
空间优化

/**
 * 对第i行的最小路径和的推导,只需要第 i-1行的 dp[i - 1][j]和 dp[i - 1][j - 1]元素即可。可以使用两个变量暂存。
 * 一维的dp数组只存储第i行的最小路径和。
 *
 * @param triangle
 * @return
 */
public static int minimumTotalOptimize(List<List<Integer>> triangle) {
    // 特判
    if (triangle == null || triangle.size() == 0) {
        return 0;
    }
    int row = triangle.size(); // 三角形行数
    int[] dp = new int[row];   // 以最大的行数创建一个一维数组
    // 初始化第一个
    dp[0] = triangle.get(0).get(0);
    // prev用来存放dp[i - 1][j-1], cur用来存放dp[i - 1][j],即 [i][j] 的两个父元素的dp
    int prev = 0, cur;
    // 遍历行,从第二行开始(row = 2)
    for (int i = 1; i < triangle.size(); i++) {
        // 从第二行开始对每一行的元素进行推导
        List<Integer> rows = triangle.get(i);
        // 循环当前层的元素
        for (int j = 0; j <= i; j++) {
            cur = dp[j];
            if (j == 0) {
                // 最左端特殊处理:dp[i - 1][j]
                dp[j] = cur + rows.get(j);
            } else if (j == i) {
                // 最右端特殊处理:dp[i - 1][j - 1]
                dp[j] = prev + rows.get(j);
            } else {
                dp[j] = Math.min(cur, prev) + rows.get(j);
            }
            prev = cur;
        }
    }

    int res = Integer.MAX_VALUE;
    // dp最后一行记录了最小路径
    for (int i = 0; i < triangle.size(); i++) {
        res = Math.min(res, dp[i]);
    }
    return res;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值