编程练习题(5)

交错字符串

给定三个字符串 s1、s2、s3,请你帮忙验证 s3 是否是由 s1 和 s2 交错 组成的

两个字符串 s 和 t 交错 的定义与过程如下,其中每个字符串都会被分割成若干 非空 子字符串:

s = s1 + s2 + ... + sn
t = t1 + t2 + ... + tm
|n - m| <= 1
交错 是 s1 + t1 + s2 + t2 + s3 + t3 + ... 或者 t1 + s1 + t2 + s2 + t3 + s3 + ...
提示:a + b 意味着字符串 a 和 b 连接。

Java解法:

 /*
    动态规划:
        f(i,j) 表示 s1 的前 i 个元素和 s2 的前 j 个元素是否能交错组成 s3 的前 i + j 个元素
        如果 s1的第 i 个元素和 s3 的第 i + j 个元素相等,那么 s1的前 i 个元素和 s2的前 j 个元素是否能交错组成 s3的前 i + j 个元素取决于
        s1的前 i - 1 个元素和 s2的前 j 个元素是否能交错组成 s3的前 i + j - 1 个元素,即此时 f(i, j) 取决于 f(i - 1, j),
        在此情况下如果 f(i - 1, j)为真,则 f(i, j) 也为真
        同样的,如果 s2的第 j 个元素和 s3的第 i + j 个元素相等并且 f(i, j - 1)为真,则 f(i, j)也为真。于是推导出动态规划转移方程:
        f(i,j)=[f(i−1,j) and s1(i−1) = s3(p)] or [f(i,j−1)and s2(j−1)=s3(p)]
        其中 p = i + j - 1。边界条件为 f(0, 0) = True
     */
    public static  boolean isInterleave(String s1, String s2, String s3) {
    
    
        if (s1.length() + s2.length() != s3.length()){
    
    
            return false;
        }
        int m = s1.length(), n = s2.length();
        boolean [][]dp = new boolean[m+1][n+1];
        dp[0][0] = true;
        for (int i = 0 ; i <= m ; i++){
    
    
            for (int j = 0 ; j <= n ; j++){
    
    
                int p = i + j - 1;
                if (i > 0){
    
    
                    dp[i][j] = dp[i][j] || (dp[i-1][j] && s1.charAt(i-1) == s3.charAt(p));
                }
                if (j > 0){
    
    
                    dp[i][j] = dp[i][j] || (dp[i][j-1] && s2.charAt(j-1) == s3.charAt(p));
                }
            }
        }
        return dp[m][n];
    }

Python解法:

# 交错字符串
class Solution:
    def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
        if len(s1) + len(s2) != len(s3):
            return False
        m, n = len(s1), len(s2)
        dp = [[False for _ in range(n+1)] for _ in range(m+1)]
        dp[0][0] = True
        for i in range(m+1):
            for j in range(n+1):
                p = i + j - 1
                if i > 0:
                    dp[i][j] = dp[i][j] or (dp[i - 1][j] and s1[i - 1] == s3[p])
                if j > 0:
                    dp[i][j] = dp[i][j] or (dp[i][j - 1] and s2[j - 1] == s3[p])
        return dp[m][n]

# 测试
s = Solution()
s1 = "aabcc"
s2 = "dbbca"
s3 = "aadbbcbcac"
print(s.isInterleave(s1, s2, s3))

# 答案:
True

验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

一个二叉搜索树具有如下特征:

  1. 节点的左子树只包含小于当前节点的数。
  2. 节点的右子树只包含大于当前节点的数。
  3. 所有左子树和右子树自身必须也是二叉搜索树。

Java解法:

 /*
    递归回溯
     */

    public static boolean isValidBST(TreeNode root) {
    
    
        if (root == null){
    
    
            return true;
        }
        return backTrack(root,null,null);
    }

    public static boolean backTrack(TreeNode root, Integer low, Integer high){
    
    
        if (root == null){
    
    
            return true;
        }
        int val = root.val;
        if (low != null && val <= low ){
    
    
            return false;
        }
        if (high != null && val >= high){
    
    
            return false;
        }
        if (!backTrack(root.left, low, val)){
    
    
            return false;
        }
        if (!backTrack(root.right,val, high)){
    
    
            return false;
        }
        return true;
    }

恢复二叉搜索树

给你二叉搜索树的根节点 root ,该树中的两个节点被错误地交换。请在不改变其结构的情况下,恢复这棵树。

Java解法:

 /*
    中序遍历,得到的值序列是递增有序的,而如果我们错误地交换了两个节点,等价于在这个值序列中交换了两个值,破坏了值序列的递增性
    因此只要我们找到这两个位置,即可找到被错误交换的两个节点

    1.找到二叉搜索树中序遍历得到值序列的不满足条件的位置。
    2.如果有两个,我们记为 i 和 j(i<j 且 ai>a{i+1} && aj>a{j+1}),那么对应被错误交换的节点即为 ai对应的节点和 a{j+1}a对应的节点,
    我们分别记为 x和 y
    3.如果有一个,我们记为 i,那么对应被错误交换的节点即为 ai对应的节点和 a{i+1}对应的节点,
    我们分别记为 x 和 y。
    4.交换 x 和 y 两个节点即可。

     */

    public static void recoverTree(TreeNode root) {
    
    
        List<Integer> nums = new ArrayList<>();
        inorder(nums, root);
        int []swap = findTwoSwapped(nums);
        recover(root, 2, swap[0], swap[1]);

    }

    public static void inorder(List<Integer> nums, TreeNode root){
    
    
        if (root == null){
    
    
            return;
        }
        inorder(nums, root.left);
        nums.add(root.val);
        inorder(nums, root.right);
    }

    public static int[] findTwoSwapped(List<Integer> nums){
    
    
        int n = nums.size();
        int x = -1, y = -1;
        for (int i = 1; i < n; i++){
    
    
            if (nums.get(i) < nums.get(i-1)){
    
    
                y = nums.get(i);
                if (x == -1){
    
    
                    x = nums.get(i-1);
                }else{
    
    
                    break;
                }
            }
        }
        return new int[]{
    
    x,y};
    }

    public static void recover(TreeNode root, int count, int x, int y) {
    
    
        if (root != null){
    
    
            if (root.val == x || root.val == y){
    
    
                root.val = root.val == x ? y : x;
                if (--count == 0){
    
    
                    return;
                }
            }
            recover(root.left, count, x, y);
            recover(root.right, count, x, y);
        }
    }

相同的树

给定两个二叉树,编写一个函数来检验它们是否相同。

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

扫描二维码关注公众号,回复: 12905733 查看本文章

Java解法:

public static boolean isSameTree(TreeNode p, TreeNode q) {
    
    
        if (p == null && q == null){
    
    
            return true;
        }else if (p == null || q == null){
    
    
            return false;
        }
        if (p.val != q.val){
    
    
            return false;
        }
        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }

对称二叉树

给定一个二叉树,检查它是否是镜像对称的。

Java解法:

public static boolean isSymmetric(TreeNode root) {
    
    
        return isSymmetric(root.left, root.right);
    }

    public static boolean isSymmetric(TreeNode left, TreeNode right) {
    
    
        if (left == null && right == null){
    
    
            return true;
        }
        if (left == null || right == null){
    
    
            return false;
        }
        return left.val == right.val && isSymmetric(left.left, right.right) && isSymmetric(left.right, right.left);
    }

二叉树的锯齿形层序遍历

给定一个二叉树,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
(可参考leetcode4的层序遍历)
Java解法:

public static List<List<Integer>> zigzagLevelOrder(TreeNode root) {
    
    
        List<List<Integer>> res = new ArrayList<>();
        if (root == null){
    
    
            return res;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        boolean flag = false;
        while (!queue.isEmpty()){
    
    
            int size = queue.size();
            Deque<Integer> list = new LinkedList<>();
            for (int i = 0 ; i < size ; i++){
    
    
                TreeNode cur = queue.peek();
                queue.poll();
                if (cur == null) {
    
    
                    continue;
                }
                if (!flag){
    
    
                    list.offerLast(cur.val);
                }else{
    
    
                    list.offerFirst(cur.val);
                }

                queue.offer(cur.left);
                queue.offer(cur.right);
            }
            if (!list.isEmpty()){
    
    
                res.add(new LinkedList<>(list));
            }
            flag = !flag;
        }
        return res;
    }

二叉树的最大深度

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

Java解法:

public static int maxDepth(TreeNode root) {
    
    
        if (root == null){
    
    
            return 0;
        }else{
    
    
            int left_high = maxDepth(root.left);
            int right_high = maxDepth(root.right);
            return Math.max(left_high, right_high) + 1;
        }
    }

从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。

/*
    二叉树前序遍历的顺序为:
        1.先遍历根节点;
        2.随后递归地遍历左子树;
        3.最后递归地遍历右子树。

    二叉树中序遍历的顺序为:
        1.先递归地遍历左子树;
        2.随后遍历根节点;
        3.最后递归地遍历右子树。
     */

    public static Map<Integer, Integer> map;

    public static TreeNode buildTree(int[] preorder, int[] inorder) {
    
    
        int n = preorder.length;
        // 构造哈希映射,快速定位根节点
        map = new HashMap<>();
        for (int i = 0; i < n; i++) {
    
    
            map.put(inorder[i], i);
        }
        return buildTree(preorder, 0, n-1, inorder, 0, n-1);
    }

    public static TreeNode buildTree(int[] preorder,  int pleft, int pright,int[] inorder, int ileft, int iright){
    
    
        if (pleft > pright){
    
    
            return null;
        }
        /* 前序遍历中的第一个节点就是根节点 */
        int p_root = pleft;
        /* 在中序遍历中定位根节点 */
        int i_root = map.get(preorder[p_root]);
        /* 先把根节点建立出来 */
        TreeNode root = new TreeNode(preorder[p_root]);
        /* 得到左子树中的节点数 */
        int size_left_subtree = i_root - ileft;
        /* 递归地构造左子树,并连接到根节点 */
        /* 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素 */
        root.left = buildTree(preorder, pleft+1, pleft+size_left_subtree, inorder, ileft, i_root-1);
        /* 递归地构造右子树,并连接到根节点 */
        /* 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素 */
        root.right = buildTree(preorder, pleft+size_left_subtree+1, pright, inorder, i_root+1, iright);
        return root;
    }

从中序与后序遍历序列构造二叉树

根据一棵树的中序遍历与后序遍历构造二叉树。

Java解法:

/*
    中序遍历的顺序是每次遍历左孩子,再遍历根节点,最后遍历右孩子。
    后序遍历的顺序是每次遍历左孩子,再遍历右孩子,最后遍历根节点。

    根据中序遍历和后续遍历的特性我们进行树的还原过程分析

    1.首先在后序遍历序列中找到根节点(最后一个元素)
    2.根据根节点在中序遍历序列中找到根节点的位置
    3.根据根节点的位置将中序遍历序列分为左子树和右子树
    4.根据根节点的位置确定左子树和右子树在中序数组和后续数组中的左右边界位置
    5.递归构造左子树和右子树
    6.返回根节点结束
     */

    public static Map<Integer, Integer> map;
    static int []post;

    public static TreeNode buildTree(int[] inorder, int[] postorder) {
    
    
        int n = inorder.length;
        // 构造哈希映射,快速定位根节点
        map = new HashMap<>();
        post = postorder;
        for (int i = 0; i < n; i++) {
    
    
            map.put(inorder[i], i);
        }
        return buildTree(0, n-1, 0, n-1);
    }

    public static TreeNode buildTree(int ileft, int iright, int pleft, int pright){
    
    
        if (ileft > iright || pleft > pright){
    
    
            return null;
        }
        /* 后序遍历中的最后一个节点就是根节点 */
        int p_root = post[pright];
        /* 在中序遍历中定位根节点 */
        int i_idx = map.get(p_root);
        /* 先把根节点建立出来 */
        TreeNode root = new TreeNode(p_root);
        /* 递归地构造左子树,并连接到根节点 */
        root.left = buildTree(ileft, i_idx-1, pleft, pleft + i_idx - ileft - 1);
        /* 递归地构造右子树,并连接到根节点 */
        root.right = buildTree(i_idx+1, iright, pleft + i_idx - ileft, pright-1);
        return root;
    }

将有序数组转换为二叉搜索树

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

Java解法:

public static TreeNode sortedArrayToBST(int[] nums) {
    
    
        return helper(nums, 0, nums.length - 1);
    }

    public static TreeNode helper(int []nums, int left, int right){
    
    
        if (left > right){
    
    
            return null;
        }
        /* 选择中间位置左边的数字作为根节 */
        int mid = (left + right) / 2;
        TreeNode root = new TreeNode(nums[mid]);
        root.left = helper(nums, left, mid - 1);
        root.right = helper(nums, mid + 1, right);
        return root;
    }

有序链表转换二叉搜索树

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树

Java解法:

static ListNode globalHead;

    public static TreeNode sortedListToBST(ListNode head) {
    
    
        globalHead = head;
        int size = getSize(head);
        return buildTree(0,size-1);
    }

    public static int getSize(ListNode head) {
    
    
        int ret = 0;
        while (head != null) {
    
    
            ret++;
            head = head.next;
        }
        return ret;
    }
    public static TreeNode buildTree(int left, int right) {
    
    
        if (left > right){
    
    
            return null;
        }
        int mid = (left + right + 1 ) / 2;
        TreeNode root = new TreeNode();
        root.left = buildTree(left, mid -1);
        root.val = globalHead.val;
        globalHead = globalHead.next;
        root.right = buildTree(mid + 1 ,right);
        return root;
    }

平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

说明: 叶子节点是指没有子节点的节点。

Java解法:

/*
自底向上递归的做法类似于后序遍历
对于当前遍历到的节点,先递归地判断其左右子树是否平衡,再判断以当前节点为根的子树是否平衡。
如果一棵子树是平衡的,则返回其高度(高度一定是非负整数),否则返回 -1。如果存在一棵子树不平衡,则整个二叉树一定不平衡。
 */

public static boolean isBalanced(TreeNode root) {
    
    
    return height(root) >= 0;
}

public static int height(TreeNode root){
    
    
    if (root == null){
    
    
        return 0;
    }
    int left_high = height(root.left);
    int right_high = height(root.right);
    if (left_high == -1 || right_high == -1 || Math.abs(left_high - right_high) > 1){
    
    
        return -1;
    }else{
    
    
        return Math.max(left_high, right_high)+1;
    }
}

二叉树的最小深度

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明: 叶子节点是指没有子节点的节点。

Java解法:

public static int minDepth(TreeNode root) {
    
    
        if (root == null){
    
    
            return 0;
        }
        if (root.left == null && root.right == null){
    
    
            return 1;
        }
        Integer min = Integer.MAX_VALUE;
        if (root.left != null){
    
    
            min = Math.min(min, minDepth(root.left));
        }
        if (root.right != null){
    
    
            min = Math.min(min, minDepth(root.right));
        }
        return min+1;
    }

路径总和

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

说明: 叶子节点是指没有子节点的节点。

Java解法:

public boolean hasPathSum(TreeNode root, int sum) {
    
    
        if (root == null){
    
    
            return false;
        }else if (root.left == null && root.right == null){
    
    
            return root.val == sum;
        }
        return hasPathSum(root.left, sum-root.val) || hasPathSum(root.right, sum-root.val);
    }

路径总和 II

给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。

说明: 叶子节点是指没有子节点的节点。

Java解法:

List<List<Integer>> ret = new LinkedList<List<Integer>>();
    Deque<Integer> path = new LinkedList<Integer>();

    public List<List<Integer>> pathSum(TreeNode root, int sum) {
    
    
        dfs(root, sum);
        return ret;
    }

    public void dfs(TreeNode root, int sum) {
    
    
        if (root == null) {
    
    
            return;
        }
        path.offerLast(root.val);
        sum -= root.val;
        if (root.left == null && root.right == null && sum == 0) {
    
    
            ret.add(new LinkedList<Integer>(path));
        }
        dfs(root.left, sum);
        dfs(root.right, sum);
        path.pollLast();
    }

二叉树展开为链表

给定一个二叉树,原地将它展开为一个单链表。

Java解法:

public void flatten(TreeNode root) {
    
    
        List<TreeNode> list = new ArrayList<TreeNode>();
        preorderTraversal(root, list);
        int size = list.size();
        for (int i = 1 ; i < size ; i++){
    
    
            TreeNode pre = list.get(i-1), cur = list.get(i);
            pre.left = null;
            pre.right = cur;
        }
    }

    public static void preorderTraversal(TreeNode root, List<TreeNode> list){
    
    
        if (root == null){
    
    
            return;
        }
        list.add(root);
        preorderTraversal(root.left, list);
        preorderTraversal(root.right, list);
    }

不同的子序列

给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。
(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)

Java解法:

 /*
    动态规划
        dp[i][j] 代表 T 前 i 字符串可以由 S j 字符串组成最多个数.
        1.S[j] == T[i]:
        此时,s: ...a,t: ..a
            s 如果用 a,那么就看 [0, i - 1] 和 [0, j - 1] 能不能有相同子序列,就是 dp[i - 1][j - 1]
            s 如果不用 a,那么就看 [0, i - 1] 和 [0, j] 能不能有相同子序列,就是 dp[i - 1][j]
        2.S[j] != T[i]:
        此时,s: ...a,t: ..b
            只能看 [0, i - 1] 和 [0, j] 能不能有相同子序列,就是 dp[i - 1][j]

     */

    public static int numDistinct(String s, String t) {
    
    
        if (s == null || s.length() == 0){
    
    
            return 0;
        }
        if (t == null || t.length() == 0){
    
    
            return s.length();
        }

        int m = t.length()+1, n = s.length() + 1;
        int [][]dp = new int[m][n];
        /*初始化,第一行, T 为空,因为空集是所有字符串子集, 所以我们第一行都是 1*/
        for (int j = 0 ; j < n ; j++){
    
    
            dp[0][j] = 1;
        }

        for (int i = 1 ; i < m ; i++){
    
    
            for (int j = i ; j < n ; j++){
    
    
                if (s.charAt(j-1) == t.charAt(i-1)){
    
    
                    dp[i][j] = dp[i-1][j-1] + dp[i][j-1];
                }else{
    
    
                    dp[i][j] = dp[i][j-1];
                }
            }
        }
        return dp[m-1][n-1];
    }

Python解法:

# 不同的子序列
# 给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
# 字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。
# (例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)

class Solution:
    def numDistinct(self, s: str, t: str) -> int:
        if not s or len(s) == 0:
            return 0
        if not t or len(t) == 0:
            return len(s)

        m = len(t) + 1
        n = len(s) + 1
        dp = [[0 for _ in range(n)] for _ in range(m)]
        # 初始化,第一行, T 为空,因为空集是所有字符串子集, 所以我们第一行都是 1
        for j in range(n):
            dp[0][j] = 1

        for i in range(1, m):
            for j in range(i, n):
                if s[j-1] == t[i-1]:
                    dp[i][j] = dp[i-1][j-1] + dp[i][j-1]
                else:
                    dp[i][j] = dp[i][j-1]

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

# 测试
d = Solution()
s = "rabbbit"
t = "rabbit"
print(d.numDistinct(s, t))

# 答案:
3

填充每个节点的下一个右侧节点指针

给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
    
    
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

Java解法:

/*
    层次遍历,把使得同一层的能够连接
     */

    public static Node connect(Node root) {
    
    
        if (root == null){
    
    
            return root;
        }
        Queue<Node> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()){
    
    
            int size = queue.size();
            for (int i = 0 ; i < size ; i++){
    
    
                Node cur = queue.poll();
                /* 连接 */
                if (i < size -1){
    
    
                    cur.next = queue.peek();
                }
                if (cur.left != null){
    
    
                    queue.offer(cur.left);
                }
                if (cur.right != null){
    
    
                    queue.offer(cur.right);
                }
            }
        }
        return root;
    }

填充每个节点的下一个右侧节点指针 II

给定一个二叉树

struct Node {
    
    
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

初始状态下,所有 next 指针都被设置为 NULL。

public Node connect(Node root) {
    
    
        if (root == null) {
    
    
            return null;
        }
        Queue<Node> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
    
    
            int n = queue.size();
            Node last = null;
            for (int i = 1; i <= n; ++i) {
    
    
                Node cur = queue.poll();
                if (cur.left != null) {
    
    
                    queue.offer(cur.left);
                }
                if (cur.right != null) {
    
    
                    queue.offer(cur.right);
                }
                if (i != 1) {
    
    
                    last.next = cur;
                }
                last = cur;
            }
        }
        return root;
    }

杨辉三角

给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。

Java解法:

 /*
    * 根据特性,规律解决
    */

    public static List<List<Integer>> generate(int numRows) {
    
    
        List<List<Integer>> res = new ArrayList<>();
        for (int i = 0 ; i < numRows ; i++){
    
    
            List<Integer> list = new ArrayList<>();
            for (int j = 0 ; j <= i ; j++){
    
    
                if (j == 0 || j == i){
    
    
                    list.add(1);
                }else{
    
    
                    list.add( res.get(i-1).get(j-1) + res.get(i-1).get((j)) );
                }
            }
            res.add(list);
        }
        return res;
    }

Python解法:

# 杨辉三角
class Solution:
    def generate(self, numRows: int) -> list:
        res = list()
        for i in range(numRows):
            cur = list()
            for j in range(0, i+1):
                if j == 0 or i == j:
                    cur.append(1)
                else:
                    cur.append(res[i-1][j] + res[i-1][j-1])
            res.append(cur)
        return res

# 测试
s = Solution()
print(s.generate(4))

# 答案:
[[1], [1, 1], [1, 2, 1], [1, 3, 3, 1]]

杨辉三角 II

给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。

Java解法:

public static List<Integer> getRow(int rowIndex) {
    
    
        //重复利用数组list,大小直接初始化为k
        List<Integer> res = new ArrayList<>(rowIndex+1);
        for (int i = 1 ; i <= rowIndex+1 ; i++){
    
    
            for(int j = i - 2 ; j > 0 ; j--){
    
    
                /* 第一个参数为下标,第二个为赋值 */
                res.set(j, res.get(j) + res.get(j-1));
            }
            res.add(1);
            System.out.println(res);
        }
        return res;
    }

Python解法:

# 三角形最小路径
class Solution:
    def getRow(self, rowIndex: int) -> list:
        res = list()
        for i in range(1, rowIndex+2):
            for j in range(i - 2, 0, -1):
                res[j] = (res[j] + res[j-1])
            res.append(1)

        return res

# 测试
s = Solution()
print(s.getRow(4))

# 答案:
[1, 4, 6, 4, 1]

三角形最小路径和

给定一个三角形 triangle ,找出自顶向下的最小路径和。

每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。

Java解法:

/*
        递归 + 记忆化:自顶向下
        (记忆化:减少重复计算)
    */
    public static int minimumTotal(List<List<Integer>> triangle) {
    
    
        Integer [][]res = new Integer[triangle.size()][triangle.size()];
        return dfs(triangle, 0, 0, res);
    }

    public static int dfs(List<List<Integer>> triangle, int i, int j, Integer [][]res){
    
    
        if (i == triangle.size()){
    
    
            return 0;
        }
        if (res[i][j] != null){
    
    
            return res[i][j];
        }
        return res[i][j] = Math.min(dfs(triangle, i+1, j, res), dfs(triangle, i+1, j+1, res)) + triangle.get(i).get(j);
    }


    /*
        动态规划:自底向上
        dp[i][j] 表示从点 (i, j) 到底边的最小路径和。
        dp[i][j]=min(dp[i+1][j], dp[i+1][j+1]) + triangle[i][j]
     */
    public static int minimumTotal2(List<List<Integer>> triangle) {
    
    
        int n = triangle.size();
        int [][]dp = new int[n+1][n+1];
        for (int i = n - 1; i >= 0; i--) {
    
    
            for (int j = 0; j <= i; j++) {
    
    
                dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j);
            }
        }
        return dp[0][0];
    }

Python解法:

# 三角形的最小路径和
class Solution:
    def minimumTotal(self, triangle: list) -> int:
        n = len(triangle)
        res = [[0 for _ in range(n+1)] for _ in range(n+1)]
        return self.dfs(triangle, 0, 0, res)

    def dfs(self, triangle: list, i: int, j: int, res: list) -> int:
        if i == len(triangle):
            return 0
        if res[i][j]:
            return res[i][j]
        res[i][j] = min(self.dfs(triangle, i+1, j, res), self.dfs(triangle, i+1, j+1, res)) + triangle[i][j]
        return res[i][j]


# 测试
s = Solution()
triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
print(s.minimumTotal(triangle))

# 答案:
11

更新中

猜你喜欢

转载自blog.csdn.net/qq_42748009/article/details/112391359