算法日记 20-22 day 回溯算法

ops/2024/11/14 1:37:39/

补这两天的博客,看看这两天的题目吧。

题目:组合总和

39. 组合总和 - 力扣(LeetCode)

        给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

  candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

        对于给定的输入,保证和为 target 的不同组合数少于 150 个。

题目分析:

        组合问题,那么就可以考虑回溯的解法。题目中需要注意的是无重复元素,并且数字可以重复取。和之前的题一样,将题目抽象为树形结构看看。

 然后就是递归五部曲。

public class Solution {IList<IList<int>> res=new List<IList<int>>();IList<int> path=new List<int>();public IList<IList<int>> CombinationSum(int[] candidates, int target) {BackTracking(candidates,target,0);return res;}public void BackTracking(int[] candidates,int target,int index){if(target<0) return;if(target==0){res.Add(new List<int>(path));return ;}for(int i=index;i<candidates.Length;i++){target-=candidates[i];path.Add(candidates[i]);BackTracking(candidates,target,i);//注意可以重复取值,所以传itarget+=candidates[i];path.RemoveAt(path.Count-1);}}
}

题目:组合总和 2

40. 组合总和 II - 力扣(LeetCode)

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。

注意:解集不能包含重复的组合

 题目分析:

        和上一题的差别在于每个数字只能用一次,而且数字可能有重复,并且不能有重复组合。

那怎么去进行去重。题目要求数子只能用一次,这里其实可以使用一个索引或者一个标志数组来判断。

先看看树形结构

图中就是使用了used的标志数组来进行去重,代码中我没有使用这种方式,不过能表示的意思是一样的。

对于used数组的使用,我们接下来用的会更加频繁。

public class Solution {IList<IList<int>> res=new List<IList<int>>();IList<int> path=new List<int>();public IList<IList<int>> CombinationSum2(int[] candidates, int target) {Array.Sort(candidates);BackTracking(candidates,target,0);return res;}public void BackTracking(int[] candidates,int target,int startIndex){if(target<0) return;if(target==0){res.Add(new List<int>(path));return;}for(int i=startIndex;i<candidates.Length;i++){if(i>startIndex&&candidates[i]==candidates[i-1]) continue;target-=candidates[i];path.Add(candidates[i]);BackTracking(candidates,target,i+1);//注意这里传的是i+1而不是StartIndex,这样可以跳过重复的值target+=candidates[i];path.RemoveAt(path.Count-1);}}
}

题目:分割回文串

131. 分割回文串 - 力扣(LeetCode)

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串。返回 s 所有可能的分割方案。 

题目分析:

        这一题无非就是两个问题,字符组合以及回文判断。

        例如对于字符串abcdef:

  • 组合问题:选取一个a之后,在bcdef中再去选取第二个,选取b之后在cdef中再选取第三个.....。
  • 切割问题:切割一个a之后,在bcdef中再去切割第二段,切割b之后在cdef中再切割第三段.....。

这么看来,其实也能形成一个树形结构。

那代码还是和其他回溯问题差不多,只是多了回文判断。

public class Solution {IList<IList<string>> res=new List<IList<string>>();IList<string> path=new List<string>();public IList<IList<string>> Partition(string s) {BackTracking(s,0);return res;}public void BackTracking(string s,int startIndex){if(startIndex>=s.Length){res.Add(new List<string>(path));return ;}for(int i=startIndex;i<s.Length;i++){if(isHuiWen(s,startIndex,i)){//回文判断path.Add(s.Substring(startIndex,i-startIndex+1));//切割字串}else continue;BackTracking(s,i+1);path.RemoveAt(path.Count-1);}}public bool isHuiWen(string str,int start,int end){for(int i=start,j=end;i<j;i++,j--){if(str[i]!=str[j]) return false;}return true;}
}

 题目:复原IP地址

93. 复原 IP 地址 - 力扣(LeetCode)

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

  • 例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245""192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。

给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

题目分析:

        和上一题差不多的道理,看看树形

 代码如下:

public class Solution {IList<string> res=new List<string>();public IList<string> RestoreIpAddresses(string s) {if (s.Length < 4 || s.Length > 12) return res;BackTracking(s,0,0);return res;}public void BackTracking(string s,int start,int pointNum){if(pointNum==3){// 判断第四段子字符串是否合法,如果合法就放进result中if(isIP(s,start,s.Length-1)){res.Add(s);}return;}for(int i=start;i<s.Length;i++){if(isIP(s,start,i)){s=s.Insert(i+1,".");BackTracking(s,i+2,pointNum+1);//加入了.,所以加2s=s.Remove(i+1,1);}else break;}}public bool isIP(string s,int start,int end){if (start > end) return false;if (s[start] == '0' && start != end) return false;int num = 0;for (int i = start; i <= end; i++){if (s[i] > '9' || s[i] < '0') return false;num = num * 10 + s[i] - '0';if (num > 255) return false;}return true;}
}

题目:子集

78. 子集 - 力扣(LeetCode)

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的

子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

 题目分析:

        还是和组合问题一个套路,不过需要注意的是,空集是任何集合的子集。

public class Solution {IList<IList<int>> res=new List<IList<int>>();IList<int> path=new List<int>();public IList<IList<int>> Subsets(int[] nums) {BackTracking(nums,0);return res;}public void BackTracking(int[] nums,int start){res.Add(new List<int>(path));//放在开头,注意空集也是子集if(start>=nums.Length) return;for(int i=start;i<nums.Length;i++){path.Add(nums[i]);BackTracking(nums,i+1);path.RemoveAt(path.Count-1);}}
}

题目:子集 2

90. 子集 II - 力扣(LeetCode)

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的 

子集(幂集)。解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

 题目分析:

        和上一题一样的,不过数组中可能会出现重复元素,不过是子集,我们只需要判断相邻两个值即可。

public class Solution {IList<IList<int>> res=new List<IList<int>>();IList<int> path=new List<int>();public IList<IList<int>> SubsetsWithDup(int[] nums) {Array.Sort(nums);BackTracking(nums,0);return res;}public void BackTracking(int[] nums,int start){res.Add(new List<int>(path));if(start>=nums.Length) return;for(int i=start;i<nums.Length;i++){if(i>start&&nums[i]==nums[i-1]) continue;path.Add(nums[i]);BackTracking(nums,i+1);path.RemoveAt(path.Count-1);}}
}

题目:递增子序列

491. 非递减子序列 - 力扣(LeetCode)

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

题目分析:

        序列需要递增,并且大于等于2,并且数组中有重复。怎么去重?

可以看到,我们实际上去重的部分是树层。这里我们可以使用hash来记录使用过的数字。

public class Solution {IList<IList<int>> res=new List<IList<int>>();IList<int> path=new List<int>();public IList<IList<int>> FindSubsequences(int[] nums) {BackTracking(nums,0);return res;}public void BackTracking(int[] nums,int start){if(path.Count>=2){res.Add(new List<int>(path));}//树层去重HashSet<int> temp=new HashSet<int>();//记录每一层已经使用的数for(int i=start;i<nums.Length;i++){if(path.Count>0&&nums[i]<path[path.Count-1]||temp.Contains(nums[i])) continue;temp.Add(nums[i]);path.Add(nums[i]);BackTracking(nums,i+1);path.RemoveAt(path.Count-1);}}
}

 题目:全排列

46. 全排列 - 力扣(LeetCode)

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

题目分析:

        画出树形图来看

 

实际上我们需要考虑的是树枝上的去重,这里就用到了之前的used数组来进行标记。

public class Solution {IList<IList<int>> res=new List<IList<int>>();IList<int> path=new List<int>();public IList<IList<int>> Permute(int[] nums) {bool[] used=new bool[nums.Length];//记录一层中,那些数被使用过BackTracking(nums,used);return res;}public void BackTracking(int[] nums,bool[] used){if(path.Count==nums.Length){res.Add(new List<int>(path));return;}for(int i=0;i<nums.Length;i++){//每次都从0开始,这样就能重复获取if(used[i])continue;//当前值被使用了,跳过used[i]=true;path.Add(nums[i]);BackTracking(nums,used);path.RemoveAt(path.Count-1);used[i]=false;}}
}

题目:全排列 2

47. 全排列 II - 力扣(LeetCode)

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

题目分析:

        比上一题多了一个重复。

 

根据树形结构来看,我们还需要对树层进行去重。

public class Solution {IList<IList<int>> res=new List<IList<int>>();IList<int> path=new List<int>();public IList<IList<int>> PermuteUnique(int[] nums) {Array.Sort(nums);bool[] used=new bool[nums.Length];BackTracking(nums,used);return res;}public void BackTracking(int[] nums,bool[] used){if(path.Count==nums.Length){res.Add(new List<int>(path));return;}for(int i=0;i<nums.Length;i++){if(i>0&&nums[i]==nums[i-1]&&!used[i-1]) continue;//树层去重,userd[i-1]==false保证是同一层if(used[i]) continue;used[i]=true;path.Add(nums[i]);BackTracking(nums,used);used[i]=false;path.RemoveAt(path.Count-1);}}
}

对于更详细的解析与其他语言的代码块,可以去代码随想录上查看。

代码随想录 (programmercarl.com)

已刷题目:70

http://www.ppmy.cn/ops/133015.html

相关文章

【智谱开放平台-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

(RK3566驱动开发 - 1).pinctrl和gpio子系统

一.设备树 pinctrl部分可以参考 rockchip 官方的绑定文档 &#xff1a;kernel/Documentation/devicetree/bindings/pinctrl PIN_BANK&#xff1a;引脚所属的组 - 本次例程使用的是 GPIO3_A1 这个引脚&#xff0c;所以所属的组为 3&#xff1b; PIN_BANK_IDX&#xff1a;引脚的…

excel功能

统计excel中每个名字出现的次数 在Excel中统计每个名字出现的次数&#xff0c;您可以使用COUNTIF函数或数据透视表。以下是两种方法的详细步骤&#xff1a; 方法一&#xff1a;使用COUNTIF函数 准备数据&#xff1a;确保您的姓名列表位于一个连续的单元格区域&#xff0c;例如…

VBA即用型代码手册:设置PDF中标题行Set Header Row in Output PDF

我给VBA下的定义&#xff1a;VBA是个人小型自动化处理的有效工具。可以大大提高自己的劳动效率&#xff0c;而且可以提高数据的准确性。我这里专注VBA,将我多年的经验汇集在VBA系列九套教程中。 作为我的学员要利用我的积木编程思想&#xff0c;积木编程最重要的是积木如何搭建…

【go从零单排】go中的range的用法

Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 概念 range 关键字用于迭代数组、切片、字符串、映射(map)或通道(channel)等数据结构的元素。range 的基本语法如下…

Redis - 持久化

Redis ⽀持RDB和AOF两种持久化机制&#xff0c;持久化功能有效地避免因进程退出造成数据丢失问题&#xff0c; 当下次重启时利⽤之前持久化的⽂件即可实现数据恢复。本章内容&#xff1a; 介绍RDB、AOF的配置和运⾏流程&#xff0c;以及控制持久化的命令&#xff0c;如bgsave和…

如何为 SeaTunnel 配置 MySQL 用户并授予权限

在使用 SeaTunnel 进行数据处理与传输时&#xff0c;保障数据源的连接与权限配置尤为重要。本文将逐步解析如何在 MySQL 中创建用于 SeaTunnel 访问的用户&#xff0c;并授予其适当的权限&#xff0c;以满足不同操作需求。 1. 创建用户 在 MySQL 中&#xff0c;创建用户是配置…

《XGBoost算法的原理推导》12-14决策树复杂度的正则化项 公式解析

本文是将文章《XGBoost算法的原理推导》中的公式单独拿出来做一个详细的解析&#xff0c;便于初学者更好的理解。 我们定义一颗树的复杂度 Ω Ω Ω&#xff0c;它由两部分组成&#xff1a; 叶子结点的数量&#xff1b;叶子结点权重向量的 L 2 L2 L2范数&#xff1b; 公式(…