首页 > 编程语言 >Day23 回溯算法Part02

Day23 回溯算法Part02

时间:2024-07-25 16:07:09浏览次数:15  
标签:target int ArrayList Part02 Day23 start candidates 回溯 path

39. 组合总和

216. 组合总和 III不同,不要求每个数字仅能使用一次。但这样很容易出现重复的结果,剪枝还是要注意。不过这道题让我更认识到把回溯问题看成是一个多叉树的遍历的问题,当遇到一个题目,先画出它的树结构,也就是代码随想录中的这张图,for循环(横向遍历)怎么做,递归(纵向遍历)又怎么去做。这就是回溯问题最重要的两个部分。

具体到这道题,可以绘制出这样的树形结构。从这张图就可以看出代码的逻辑,for循环和递归。具体看注释

class Solution {
    public List<List<Integer>> ans = new ArrayList();
    public List<Integer> path = new ArrayList();
    public int[] candidates;
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        this.candidates = candidates;
        backTracking(0, target);
        return ans;
    }

    public void backTracking(int start, int target){ // start记录当前调用可以横向遍历的第一个元素的下标
        if(target < 0) return; //剪枝
        if(target == 0) { //本题对于path的长度并没有限制
            ans.add(new ArrayList(path));
            return;
        }
        for(int i = start; i < candidates.length; i++){
            path.add(candidates[i]);
            backTracking(i, target-candidates[i]); //注意看这个递归的参数,第一位为i,代表了元素可以重复
            path.remove(path.size()-1);
        }
    }
}

40. 组合总和 II

这道题更是体现了绘制树结构的重要性。需要注意的是,本题的candidates数组中会出现重复元素,每个数字在每个组合中仅能出现一次。为了更容易的去重复,像这样的题目第一步都需要先对数组进行排序。回溯在处理后的数组上进行。

树结构如下,不需要去管used数组,这里是代码随想录中给出的使用数组记录访问过元素的方法,不用也是可以的。注意看两个蓝色箭头的地方,这两个取1若反映在调用代码上,他们肯定是完全相同的,都是backTracking(1, target),但很显然,左边箭头的部分是必要的,而右边箭头(即和上一个1在同一层)是不必要的,否则会出现重复。

然而反映在递归的代码中,这两个的调用是完全相同的。我们只能在for循环中删除第二个1的调用。具体看注释

40.组合总和II
class Solution {
    List<List<Integer>> ans = new ArrayList();
    List<Integer> path = new ArrayList();
    int[] candidates;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        this.candidates = candidates;
        Arrays.sort(candidates);
        backTracking(0, target);
        return ans;
    }
    public void backTracking(int start, int target){
        if(target == 0){
            ans.add(new ArrayList(path));
            return;
        }
        //由于数组已经排序,因此当target-candidates[i] < 0,i之后的数字一定也满足这个条件,直接跳出可以避免这些调用
        for(int i = start; i < candidates.length && target-candidates[i] >= 0; i++){ 
            if(i != start && candidates[i-1] == candidates[i]) continue; //同一层的调用中需要去重
            path.add(candidates[i]);
            backTracking(i+1, target-candidates[i]); //不同层是可以重复的,就如上图中的1,1
            path.remove(path.size()-1);
        }
    }
}

131. 分割回文串

目前还不会动态规划,所以只是用回溯来做。下面简单说我的思路

分割其实也可以看作是组合问题,不过组合的是分割点的位置。分割点间就形成了子串,需要判断他们是否是回文的,从而确定分割点的正确性。所以回溯其实也就是遍历了所有分割点的组合,将生成了不是回文子串的分割方式舍弃。

class Solution {
    List<List<String>> ans = new ArrayList();
    List<String> substrings = new ArrayList();
    String s;
    public List<List<String>> partition(String s) {
        this.s = s;
        findSeperate(0);
        return ans;
    }
    public void findSeperate(int start){ //start记录未被分割的部分的起始index
        if(start == s.length()) { //说明前面的所有分割点都是满足要求的,所以可以保存结果
            ans.add(new ArrayList(substrings));
            return;
        }
        for(int i = start+1; i <= s.length(); i++){ //搜索分割点
            String sub = s.substring(start, i);
            if(!isPalindrome(sub)) continue;
            substrings.add(sub);
            findSeperate(i); //若当前分割点满足要求,继续分割后序部分
            substrings.removeLast();
        }
    }
    public boolean isPalindrome(String s){
        int left = 0, right = s.length()-1;
        while(left < right){
            if(s.charAt(left) != s.charAt(right)) return false;
            left++;
            right--;
        }
        return true;
    }
}

标签:target,int,ArrayList,Part02,Day23,start,candidates,回溯,path
From: https://www.cnblogs.com/12sleep/p/18323415

相关文章

  • 「代码随想录算法训练营」第二十天 | 回溯算法 part2
    39.组合总和题目链接:https://leetcode.cn/problems/combination-sum/题目难度:中等文章讲解:https://programmercarl.com/0039.组合总和.html视频讲解:https://www.bilibili.com/video/BV1KT4y1M7HJ题目状态:久违的通过!思路:使用回溯模板,在单层循环时判断当前数组值是否大于......
  • 代码随想录算法训练营第二十二天|回溯算法part01
    第77题.组合在单个集合1-n中,寻找所有个数为k的组合。和所有递归一样,都有三部曲。确定参数与返回值确定终止条件单层逻辑首先,回溯算法的返回值一般是void,参数依照题目要求而增加,在这一题中,参数有n,k还有startIndex。终止条件是path的size等于k,将path存放在result中。......
  • Day 22 回溯算法 part01
    77.组合我的这个解法还算挺简单易懂的,还是看注释classSolution{List<List<Integer>>ans=newArrayList();//存储最终结果集合List<Integer>tmp=newArrayList();//记录单次的pathpublicList<List<Integer>>combine(intn,intk){backTr......
  • 「代码随想录算法训练营」第十九天 | 回溯算法 part01
    回溯算法模板voidbacktracking(参数){if(终止条件){存放结果;return;}for(选择:本层集合中元素(树中节点孩子的数量就是集合的大小)){处理节点;backtracking(路径,选择列表);//递归回溯,撤销处理结果}}......
  • DAY6 哈希表part02
     今日任务● 454.四数相加II● 383.赎金信● 15.三数之和● 18.四数之和● 总结454.四数相加II题目链接/文章讲解/视频讲解:https://programmercarl.com/0454.%E5%9B%9B%E6%95%B0%E7%9B%B8%E5%8A%A0II.html1classSolution{2public:3intfo......
  • 代码随想录训练营 Day4打卡 链表part02 24. 两两交换链表中的节点 19.删除链表的倒数
    代码随想录训练营Day4打卡链表part02一、力扣24.两两交换链表中的节点给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。示例1:输入:head=[1,2,3,4]输出:[2,1,4,3]算法思路:引入虚......
  • DAY4 链表part02
     今日任务● 24.两两交换链表中的节点● 19.删除链表的倒数第N个节点● 面试题02.07.链表相交● 142.环形链表II● 总结  //有一定难度,需要好好琢磨24.两两交换链表中的节点用虚拟头结点,这样会方便很多。题目链接/文章讲解/视频讲解:https://progr......
  • 【C++BFS 回溯】756. 金字塔转换矩阵
    本文涉及知识点C++BFS算法C++回溯LeetCode756.金字塔转换矩阵你正在把积木堆成金字塔。每个块都有一个颜色,用一个字母表示。每一行的块比它下面的行少一个块,并且居中。为了使金字塔美观,只有特定的三角形图案是允许的。一个三角形的图案由两个块和叠在上面的单......
  • 递归回溯法-八皇后问题
    一、问题描述八皇后问题是回溯算法的典型案例。该问题由国际西洋棋手马克斯•贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后不能处于同一行、同一列或同一斜线上,问有多少种摆法。通过计算可以得出共有92种摆法。二、思路分析由于规......
  • 代码随想录算法训练营第27天 | 回溯3:93.复原IP地址、78.子集、90.子集II
    代码随想录算法训练营第27天|回溯3:93.复原IP地址、78.子集、90.子集II93.复原IP地址https://leetcode.cn/problems/restore-ip-addresses/submissions/547344868/代码随想录https://programmercarl.com/0093.复原IP地址.html#算法公开课78.子集https://leetcode.cn/probl......