首页 > 编程语言 >【代码随想录Day23】回溯算法Part02

【代码随想录Day23】回溯算法Part02

时间:2024-09-21 16:55:17浏览次数:9  
标签:路径 int sum Part02 随想录 Day23 startIndex candidates 回溯

39. 组合总和

题目链接/文章讲解:代码随想录
视频讲解:带你学透回溯算法-组合总和(对应「leetcode」力扣题目:39.组合总和)| 回溯法精讲!_哔哩哔哩_bilibili

class Solution {
    // 存储最终结果的列表
    List<List<Integer>> result = new ArrayList<>();
    // 存储当前路径的链表
    LinkedList<Integer> path = new LinkedList<>();
    // 当前路径的和
    int sum = 0;

    // 主函数,用于调用回溯函数并返回结果
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        // 对候选数组进行排序,以便后续处理
        Arrays.sort(candidates);
        // 调用回溯函数,从第一个元素开始
        backtracing(candidates, target, 0);
        // 返回最终结果
        return result;
    }

    // 回溯函数,用于递归地寻找组合
    public void backtracing(int[] candidates, int target, int startIndex) {
        // 如果当前路径的和大于目标值,直接返回
        if (sum > target) {
            return;
        }
        // 如果当前路径的和等于目标值,将当前路径添加到结果中
        if (sum == target) {
            result.add(new ArrayList<>(path));
            return;
        }
        // 从startIndex开始遍历候选数组
        for (int i = startIndex; i < candidates.length; i++) {
            // 将当前候选值添加到路径和sum中
            sum += candidates[i];
            // 将当前候选值添加到路径中
            path.add(candidates[i]);
            // 递归调用回溯函数,继续寻找组合
            backtracing(candidates, target, i);
            // 递归返回后,从sum中减去当前候选值
            sum -= candidates[i];
            // 从路径中移除最后一个元素
            path.removeLast();
        }
    }
}

40.组合总和II

题目链接/文章讲解:代码随想录
视频讲解:回溯算法中的去重,树层去重树枝去重,你弄清楚了没?| LeetCode:40.组合总和II_哔哩哔哩_bilibili

class Solution {
    // 存储最终结果的列表
    List<List<Integer>> result = new ArrayList<>();
    // 存储当前路径的链表
    LinkedList<Integer> path = new LinkedList<>();
    // 当前路径的和
    int sum = 0;

    // 主函数,用于调用回溯函数并返回结果
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        // 对候选数组进行排序,以便后续处理
        Arrays.sort(candidates);
        // 调用回溯函数,从第一个元素开始
        backtracing(candidates, target, 0);
        // 返回最终结果
        return result;
    }

    // 回溯函数,用于递归地寻找组合
    public void backtracing(int[] candidates, int target, int startIndex) {
        // 如果当前路径的和大于目标值,直接返回
        if (sum > target) {
            return;
        }
        // 如果当前路径的和等于目标值,将当前路径添加到结果中
        if (sum == target) {
            result.add(new ArrayList<>(path));
            return;
        }
        // 从startIndex开始遍历候选数组
        for (int i = startIndex; i < candidates.length; i++) {
            // 剪枝操作:如果当前候选值与前一个候选值相同,并且前一个候选值还没有被使用过,跳过当前候选值
            if (i > startIndex && candidates[i] == candidates[i - 1]) {
                continue;
            }
            // 将当前候选值添加到路径和sum中
            sum += candidates[i];
            // 将当前候选值添加到路径中
            path.add(candidates[i]);
            // 递归调用回溯函数,从下一个元素开始(避免重复使用同一个数字)
            backtracing(candidates, target, i + 1);
            // 递归返回后,从sum中减去当前候选值
            sum -= candidates[i];
            // 从路径中移除最后一个元素
            path.removeLast();
        }
    }
}

在解决组合问题时,如 combinationSum2 这类题目,输入的 candidates 数组可能会包含重复的数字。当题目要求返回的组合是唯一的(即不能包含重复的组合),我们需要采取一定的措施来避免生成重复的结果。

当对 candidates 数组进行排序后,相同的数字会相邻排列。这样,在遍历过程中,如果当前考虑的数字与前一个数字相同,并且前一个数字已经在这个位置被考虑过(即在当前路径中已经被选择过),那么就没有必要再考虑当前这个相同的数字了,因为这样做会导致重复的解。

具体来说,剪枝操作 if (i > startIndex && candidates[i] == candidates[i - 1]) 的目的是跳过所有与前一个已检查过的元素相同的元素。这里的关键是 i > startIndex 这个条件,它确保了只有在当前索引 i 不是 startIndex 的时候才进行跳过操作。这是因为在第一次进入循环的时候,我们希望检查 startIndex 位置的所有可能,包括那些重复的数字。但是在那之后,为了避免重复,我们需要跳过所有与 startIndex 位置上数字相同的后续数字。

通过这种方式,我们保证了每种情况下的唯一性,同时减少了不必要的计算,提高了算法效率。

131.分割回文串

题目链接/文章讲解:代码随想录
视频讲解:代码随想录

class Solution {
    // 存储所有有效的回文分割组合
    List<List<String>> result = new ArrayList<>();
    // 存储当前的回文分割路径
    LinkedList<String> path = new LinkedList<>();

    // 主方法,接收字符串 s 并开始回溯
    public List<List<String>> partition(String s) {
        backtracing(s, 0); // 从索引 0 开始进行回溯
        return result; // 返回所有有效的分割组合
    }

    // 回溯方法,负责生成回文分割
    public void backtracing(String s, int startIndex) {
        // 如果开始索引超过字符串的长度,说明已生成一个有效组合
        if (startIndex >= s.length()) {
            result.add(new ArrayList<>(path)); // 将当前路径的副本添加到结果中
            return; // 返回以结束当前递归
        }

        // 遍历从 startIndex 到字符串的每个可能结束索引 i
        for (int i = startIndex; i < s.length(); i++) {
            // 获取子串
            String substring = s.substring(startIndex, i + 1);
            // 检查该子串是否为回文
            if (check(substring)) {
                path.add(substring); // 如果是回文,将其加入当前路径
                backtracing(s, i + 1); // 递归调用,处理下一个子串的起始索引
                path.removeLast(); // 回溯,移除最后一个添加的子串,恢复路径
            }
        }
    }

    // 检查一个字符串是否为回文
    public boolean check(String s) {
        // 对称比较字符串的字符
        for (int i = 0; i < s.length() / 2; i++) {
            // 如果发现不相等的字符,返回 false
            if (s.charAt(i) != s.charAt(s.length() - 1 - i)) {
                return false;
            }
        }
        // 如果全部字符相同,返回 true,表示是回文
        return true;
    }
}

标签:路径,int,sum,Part02,随想录,Day23,startIndex,candidates,回溯
From: https://blog.csdn.net/weixin_43724673/article/details/142419617

相关文章

  • 代码随想录算法训练营第一天 | 209. 长度最小的子数组 59. 螺旋矩阵 58. 区间和 Java
    209.长度最小的子数组题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/description/解题思路思路1:暴力解法通过两个for循环,找出所有的可能的区间,并比较出最小区间思路2:滑动窗口因为需要找出的是连续的一个子数组,所以可以模拟一个从左到右滑动的一个......
  • leetcode刷题day23|回溯算法Part02(39. 组合总和 、40.组合总和II、131.分割回文串)
    39.组合总和思路:这个题与77.组合的差异在于元素可以无限制重复被选取,那么只需要更改startIndex即可,每一层递归都可以从头选用元素。回溯三部曲与77.组合基本一致。代码如下:classSolution{List<List<Integer>>result=newArrayList<>();List<Integer>pa......
  • 代码随想录算法 - 回溯3
    明天开始建模比赛,没时间写思路了题目193.复原IP地址有效IP地址正好由四个整数(每个整数位于0到255之间组成,且不能含有前导0),整数之间用'.'分隔。例如:"0.1.2.201"和"192.168.1.1"是有效IP地址,但是"0.011.255.245"、"192.168.1.312"和"[email protected]"是无......
  • 代码随想录算法训练营,9月20日 | 93.复原IP地址,78.子集,90.子集II
    93.复原IP地址题目链接:93.复原IP地址文档讲解︰代码随想录(programmercarl.com)视频讲解︰复原IP地址日期:2024-09-20Java代码如下:classSolution{List<String>res=newArrayList<>();privatevoidbackTracking(Strings,intstartIndex,intpointNum){......
  • 代码随想录训练营第38天|string虚拟头
    322.零钱兑换classSolution{public:intcoinChange(vector<int>&coins,intamount){vector<int>dp(amount+1,INT_MAX);dp[0]=0;for(auto&coin:coins){for(inti=1;i<=amount;i++){......
  • 代码随想录算法训练营第十六天 | Javascript | 力扣Leetcode | 回溯 | 77. 组合、216.
    目录前言简介题目链接:77.组合题目链接:216.组合总和3题目链接:17.电话号码的字母组合前言踏平坎坷成大道,斗罢艰险又出发!自律的尽头是自控,自控的尽头是硬控。愿道友们披荆斩棘,终能得偿所愿。简介本人是小几年经验的前端开发,算法基础只有力扣几十道题,非常薄......
  • 代码随想录算法训练营第十五天 | Javascript | 继续二叉树的一天 | 力扣Leetcode | 补
    目录前言简介题目链接:501.二叉搜索树中的众数题目链接:236.二叉树的最近公共祖先题目链接:235.二叉搜索树的最近公共祖先前言踏平坎坷成大道,斗罢艰险又出发!自律的尽头是自控,自控的尽头是硬控。愿道友们披荆斩棘,终能得偿所愿。简介本人是小几年经验的前端开发,......
  • 代码随想录 -- 二叉树 -- 把二叉搜索树转换为累加树
    538.把二叉搜索树转换为累加树-力扣(LeetCode)思路:定义pre变量用来记录当前节点的前一个节点(右中左顺序遍历)的值。递归出口:当root为空时,return。单层递归逻辑:(右中左)右:self.tra(root.right)中:令root的值为它本身加上pre,更新pre为当前root的值;左:self.tra(root.left)class......
  • 代码随想录 -- 二叉树 -- 将有序数组转换为二叉搜索树
    108.将有序数组转换为二叉搜索树-力扣(LeetCode)思路:(注意题目要求是平衡二叉树!!!)递归出口:当传入数组为空时,返回空。单层递归逻辑:找到数组中间的值,令其为root,数组左边为root的左子树,数组右边为root的右子树。最后返回root。classSolution(object):defsortedArrayTo......
  • 代码随想录 -- 二叉树 -- 修剪二叉搜索树
     669.修剪二叉搜索树-力扣(LeetCode)思路:递归出口:当root为空时,返回空。当root的值比low小时,如果root没有右子树,直接返回空;否则返回trimBST(root.right,low,high)。当root的值比high大时,如果root没有左子树,直接返回空;否则返回trimBST(root.left,low,high)。单层递归逻辑:......