Android学习总结之算法篇八(路径总和和组合总和)

路径总和

import java.util.ArrayList;
import java.util.List;

// 定义二叉树节点类
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    // 构造函数,用于初始化节点值
    TreeNode(int x) {
        val = x;
    }
}

public class PathSumProblems {

    // 路径总和 I:判断是否存在从根节点到叶子节点的路径,使得路径上所有节点值的和等于给定的目标值
    public boolean hasPathSumI(TreeNode root, int sum) {
        // 若根节点为空,不存在满足条件的路径
        if (root == null) {
            return false;
        }
        // 若当前节点为叶子节点,判断当前节点值是否等于剩余的和
        if (root.left == null && root.right == null) {
            return root.val == sum;
        }
        // 递归在左子树和右子树中查找
        return hasPathSumI(root.left, sum - root.val) || hasPathSumI(root.right, sum - root.val);
    }

    // 路径总和 II:找出所有从根节点到叶子节点的路径,使得路径上所有节点值的和等于给定的目标值
    public List<List<Integer>> pathSumII(TreeNode root, int sum) {
        List<List<Integer>> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        dfsII(root, sum, new ArrayList<>(), result);
        return result;
    }

    // 深度优先搜索辅助方法,用于路径总和 II
    private void dfsII(TreeNode node, int remainingSum, List<Integer> currentPath, List<List<Integer>> result) {
        if (node == null) {
            return;
        }
        // 将当前节点加入路径
        currentPath.add(node.val);
        remainingSum -= node.val;
        // 若当前节点为叶子节点且剩余和为 0,说明找到了一条满足条件的路径
        if (node.left == null && node.right == null && remainingSum == 0) {
            result.add(new ArrayList<>(currentPath));
        }
        // 递归遍历左子树和右子树
        dfsII(node.left, remainingSum, currentPath, result);
        dfsII(node.right, remainingSum, currentPath, result);
        // 回溯,移除当前节点
        currentPath.remove(currentPath.size() - 1);
    }

    // 路径总和 III:计算二叉树中路径和等于给定值的路径总数,路径不需要从根节点开始,也不需要在叶子节点结束
    public int pathSumIII(TreeNode root, int sum) {
        if (root == null) {
            return 0;
        }
        // 以当前节点为起点的路径数量
        int pathsFromRoot = countPathsIII(root, sum);
        // 左子树中的路径数量
        int pathsInLeft = pathSumIII(root.left, sum);
        // 右子树中的路径数量
        int pathsInRight = pathSumIII(root.right, sum);
        return pathsFromRoot + pathsInLeft + pathsInRight;
    }

    // 辅助方法,用于计算以当前节点为起点的路径数量
    private int countPathsIII(TreeNode node, int remainingSum) {
        if (node == null) {
            return 0;
        }
        int paths = 0;
        // 若当前节点值等于剩余和,说明找到了一条路径
        if (node.val == remainingSum) {
            paths++;
        }
        // 递归在左子树和右子树中查找
        paths += countPathsIII(node.left, remainingSum - node.val);
        paths += countPathsIII(node.right, remainingSum - node.val);
        return paths;
    }

    public static void main(String[] args) {
        // 构建一个简单的二叉树
        TreeNode root = new TreeNode(10);
        root.left = new TreeNode(5);
        root.right = new TreeNode(-3);
        root.left.left = new TreeNode(3);
        root.left.right = new TreeNode(2);
        root.right.right = new TreeNode(11);
        root.left.left.left = new TreeNode(3);
        root.left.left.right = new TreeNode(-2);
        root.left.right.right = new TreeNode(1);

        PathSumProblems solution = new PathSumProblems();

        // 测试路径总和 I
        int targetSumI = 8;
        boolean resultI = solution.hasPathSumI(root, targetSumI);
        System.out.println("路径总和 I:是否存在路径和为 " + targetSumI + " 的路径: " + resultI);

        // 测试路径总和 II
        int targetSumII = 8;
        List<List<Integer>> resultII = solution.pathSumII(root, targetSumII);
        System.out.println("路径总和 II:路径和为 " + targetSumII + " 的所有路径: " + resultII);

        // 测试路径总和 III
        int targetSumIII = 8;
        int resultIII = solution.pathSumIII(root, targetSumIII);
        System.out.println("路径总和 III:路径和为 " + targetSumIII + " 的路径总数: " + resultIII);
    }
}    

组合总和

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class CombinationSumProblems {

    // 组合总和 I:找出 candidates 中所有可以使数字和为 target 的组合,candidates 中的数字可以无限制重复被选取
    public List<List<Integer>> combinationSumI(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        if (candidates == null || candidates.length == 0) {
            return result;
        }
        // 调用回溯方法
        backtrackI(candidates, target, 0, new ArrayList<>(), result);
        return result;
    }

    private void backtrackI(int[] candidates, int remaining, int start, List<Integer> current, List<List<Integer>> result) {
        // 如果剩余值为 0,说明找到了一个有效的组合
        if (remaining == 0) {
            result.add(new ArrayList<>(current));
            return;
        }
        // 如果剩余值小于 0,说明当前组合不满足条件
        if (remaining < 0) {
            return;
        }
        // 从 start 开始遍历 candidates 数组
        for (int i = start; i < candidates.length; i++) {
            // 将当前数字加入组合
            current.add(candidates[i]);
            // 递归调用,由于数字可以重复使用,下一次递归的 start 仍然是 i
            backtrackI(candidates, remaining - candidates[i], i, current, result);
            // 回溯,移除最后一个数字
            current.remove(current.size() - 1);
        }
    }

    // 组合总和 II:找出 candidates 中所有可以使数字和为 target 的组合,candidates 中的每个数字在每个组合中只能使用一次
    public List<List<Integer>> combinationSumII(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        if (candidates == null || candidates.length == 0) {
            return result;
        }
        // 对数组进行排序,方便去重
        Arrays.sort(candidates);
        // 调用回溯方法
        backtrackII(candidates, target, 0, new ArrayList<>(), result);
        return result;
    }

    private void backtrackII(int[] candidates, int remaining, int start, List<Integer> current, List<List<Integer>> result) {
        // 如果剩余值为 0,说明找到了一个有效的组合
        if (remaining == 0) {
            result.add(new ArrayList<>(current));
            return;
        }
        // 如果剩余值小于 0,说明当前组合不满足条件
        if (remaining < 0) {
            return;
        }
        // 从 start 开始遍历 candidates 数组
        for (int i = start; i < candidates.length; i++) {
            // 跳过重复的数字,避免结果中出现重复组合
            if (i > start && candidates[i] == candidates[i - 1]) {
                continue;
            }
            // 将当前数字加入组合
            current.add(candidates[i]);
            // 递归调用,下一次递归的 start 为 i + 1,因为每个数字只能使用一次
            backtrackII(candidates, remaining - candidates[i], i + 1, current, result);
            // 回溯,移除最后一个数字
            current.remove(current.size() - 1);
        }
    }

    // 组合总和 III:找出所有相加之和为 n 的 k 个数的组合,组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字
    public List<List<Integer>> combinationSumIII(int k, int n) {
        List<List<Integer>> result = new ArrayList<>();
        // 调用回溯方法
        backtrackIII(k, n, 1, new ArrayList<>(), result);
        return result;
    }

    private void backtrackIII(int k, int remaining, int start, List<Integer> current, List<List<Integer>> result) {
        // 如果当前组合的长度等于 k 且剩余值为 0,说明找到了一个有效的组合
        if (current.size() == k && remaining == 0) {
            result.add(new ArrayList<>(current));
            return;
        }
        // 如果当前组合的长度大于 k 或者剩余值小于 0,说明当前组合不满足条件
        if (current.size() > k || remaining < 0) {
            return;
        }
        // 从 start 开始遍历 1 - 9 的数字
        for (int i = start; i <= 9; i++) {
            // 将当前数字加入组合
            current.add(i);
            // 递归调用,下一次递归的 start 为 i + 1,避免重复使用数字
            backtrackIII(k, remaining - i, i + 1, current, result);
            // 回溯,移除最后一个数字
            current.remove(current.size() - 1);
        }
    }

    public static void main(String[] args) {
        CombinationSumProblems solution = new CombinationSumProblems();

        // 测试组合总和 I
        int[] candidatesI = {2, 3, 6, 7};
        int targetI = 7;
        List<List<Integer>> resultI = solution.combinationSumI(candidatesI, targetI);
        System.out.println("组合总和 I:和为 " + targetI + " 的所有组合: " + resultI);

        // 测试组合总和 II
        int[] candidatesII = {10, 1, 2, 7, 6, 1, 5};
        int targetII = 8;
        List<List<Integer>> resultII = solution.combinationSumII(candidatesII, targetII);
        System.out.println("组合总和 II:和为 " + targetII + " 的所有组合: " + resultII);

        // 测试组合总和 III
        int k = 3;
        int n = 9;
        List<List<Integer>> resultIII = solution.combinationSumIII(k, n);
        System.out.println("组合总和 III:和为 " + n + " 的 " + k + " 个数的所有组合: " + resultIII);
    }
}    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值