欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 文化 > [Lc4_dfs] 找出所有子集的异或求和 | 全排列 II | 电话号码的字母组合

[Lc4_dfs] 找出所有子集的异或求和 | 全排列 II | 电话号码的字母组合

2026/5/2 17:48:36 来源:https://blog.csdn.net/2301_80171004/article/details/146534475  浏览:    关键词:[Lc4_dfs] 找出所有子集的异或求和 | 全排列 II | 电话号码的字母组合

目录

1.找出所有子集的异或总和再求和

题解

⭕2.全排列 II

题解

3.电话号码的字母组合

题解


1.找出所有子集的异或总和再求和

链接:1863. 找出所有子集的异或总和再求和

一个数组的 异或总和 定义为数组中所有元素按位 XOR 的结果;如果数组为 ,则异或总和为 0

  • 例如,数组 [2,5,6]异或总和2 XOR 5 XOR 6 = 1

给你一个数组 nums ,请你求出 nums 中每个 子集异或总和 ,计算并返回这些值相加之

注意:在本题中,元素 相同 的不同子集应 多次 计数。

数组 a 是数组 b 的一个 子集 的前提条件是:从 b 删除几个(也可能不删除)元素能够得到 a

示例 1:

输入:nums = [1,3]
输出:6
解释:[1,3] 共有 4 个子集:
- 空子集的异或总和是 0 。
- [1] 的异或总和为 1 。
- [3] 的异或总和为 3 。
- [1,3] 的异或总和为 1 XOR 3 = 2 。
0 + 1 + 3 + 2 = 6

题解

  • 先找出所有子集
  • 然后把每个子集异或的和加起来返回去。

算法原理:

这道题和我们上一道思路完全是一模一样,

  • 先画出决策树
  • 设计代码
    • 全局变量
    • 递归函数
    • 细节:回溯、剪枝、递归出口

因为我们有上一道题的基础,我们直接就画出决策树

这里我们需要两个全局变量

  • 一个path记录到沿途每个子集的异或
  • 然后sum负责每个字节的异或和加起来。

dfs函数,还是需要一个pos记录当前异或的位置,dfs(nums,pos),回溯利用异或的规则,两个相同的数异或为0。

  • 这里也没有剪枝, 递归出口 循环结束就是递归出口。
class Solution {
public:int path=0;int ret=0;int subsetXORSum(vector<int>& nums) {//八个子集 异或和dfs(nums,0);return ret;}void dfs(vector<int>& nums,int pos){ret+=path; //放在循环外for(int i=pos;i<nums.size();i++){path ^=nums[i];dfs(nums,i+1);//通过 i 来处理path ^=nums[i];//huan yuan}}
};

⭕2.全排列 II

链接:47. 全排列 II

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

示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],[1,2,1],[2,1,1]]

示例 2:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

题解

  • 重复的数全排列后会有重复的结果
  • 这道题就是要求去掉重复 之后的全排列

算法原理:

这道题几乎和全排列1 一模一样,我们就不在细说那些决策树怎么画,代码应该怎么写等等。

这里主要就是剪枝的问题。

  • 下面我们边画决策树变分析问题,把全排列所有不重复不漏的情况画出来,越详细越好。
  • 例如 [1,1,1,2]
  • 我们只用关心四个位置,每个位置每次从数组中4个数选择一个树放到一个位置上就行了。

只用选四次就行了。

  • 第一次可以选第一个1、第二个1、第三个1、2,但是注意这里就存在剪枝的问题了,如果第一个位置还把第二个1和第三个1选上,此时就会存在重复问题!因为后面三个位置是从112中选的。

此时就出现了第一种剪枝情况

  • 同一个节点的所有分支中,相同的元素只能选择一次

然后我们再往下走,第二个位置也可以从数组中4个数字中选任意一个。

  • 但是第一个1我们要把它剪掉,因为第一个位置已经把第一个1选过了,只能选一次。
  • 此时就有了第二种剪枝情况,这个是和全排列1一模一样的。

同一个位置的数,相同的数 只能使用一次

  • 还是用bool类型的check数组可以实现,check[i] = true 表明已经使用过了,
  • check[i] = false 说明还没有使用过。

但是这里还会出现剪枝情况,第一个1不能用,那我可以把第二个1放在第二个位置

但是第三个1不能出现了,因为同一个节点分支,相同的数只能出现一次

接下来我们就不画了,我们就可以写代码了。代码逻辑和全排列1几乎一模一样,这里我们主要分析,剪枝应该怎么写。

剪枝可以从两个角度写。其实就是两个if判定语句。

  • 1.只关心 “不合法” 的分支。 不合法的直接不让递归下去
  • 2.只关心 “合法” 的分支 合法的就递归

虽然是两个角度,但是最终得到结果都是一样的。

只关心 “不合法” 的分支

  • 当有一个位置已经选了这个数了下一个位置就不能在选这个位置,check[i] == true
  • 属于同一个分支节点,前面的数和当前的数相等 就不能选当前的数了。nums[i] == nums[i-1],因此这个条件最终是 i != 0 && nums[i] == nums[i-1] && check[i-1] == false

把上面两个条件组合在一起就得到只关心 “不合法” 的分支

只关心 “合法” 的分支

  • 首先是要 没被选过, 当一个数没人选的时候可以选这个数 check[i] == false
  • 还要满足 3 个中的一个
    • 首节点 i==0
    • 和前一个节点不一样 num[i]!=num[i-1]
    • 前一个已经被选过了 check[i-1]==true


3.电话号码的字母组合

链接:17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。


题解

  • 就是数字对应的字符串进行排列组合。
  • 对于这种搜索啊、暴搜啊,我们已经知道要用到递归,回溯、剪枝了。

有了前面的基础,这个题我们就不写那么步骤了,画出决策树,我们直接写出对应需要的东西。

  • 不过在此之前我们需要先将数字与字符串映射关系搞和,我们可以用哈希表映射
  • 或者其他方法,这里最简单的就是弄一个字符串数组把数字和字符串映射一下。

接下来就是画出决策树然后分析一下

  • 首先需要两个全局变量 ret记录结果,path记录每条路径到叶子节点的组合
  • 递归函数 给我一个digits和数字然后递归函数把组合后的结果返回出来,相信它一定能完成任务。dfs(digits,pos)
  • 然后回溯 记得恢复现场,递归出口 到叶子节点,这道题没有剪枝。
class Solution {
public:vector<string> ret;string path;vector<string> hash={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};vector<string> letterCombinations(string digits) {if (digits.empty()) return {}; // 处理空输入的特殊情况dfs(digits,0);return ret;}void dfs(string digits,int pos){if(path.size()==digits.size()){ret.push_back(path);return;}int num = digits[pos] - '0';for (char c : hash[num]) {path.push_back(c);dfs(digits,pos+1); //pos+1 实现移动path.pop_back();}}
};

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词