递归 与 dfs 综合练习(一)

news2024/12/25 23:49:33

目录

一、找出所有子集的异或总和再求和

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

2.题目描述:

3.解法(递归)

🌵算法思路:

🌵算法代码:

二、全排列 II

1.题目链接:47. 全排列 II

2.题目描述:

3.解法

🌴算法思路:

🌴算法代码:

三、电话号码的字母组合

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

2.题目描述:

3.解法

🍓算法思路:

🍓算法代码:

四、括号生成

1.题目链接:22. 括号生成

2.题目描述:

3.解法

🍍算法思路:

🍍算法代码:


一、找出所有子集的异或总和再求和

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

2.题目描述:

一个数组的 异或总和 定义为数组中所有元素按位 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

示例 2:

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

示例 3:

输入:nums = [3,4,5,6,7,8]
输出:480
解释:每个子集的全部异或总和值之和为 480 。

3.解法(递归

🌵算法思路:

        所有子集可以解释为:每个元素选择在或不在一个集合中(因此,子集有 2^n 个)。本题我们需要求出所有子集,将它们的异或和相加。因为异或操作满足交换律,所以我们可以定义一个变量,直接记录当前状态的异或和。使用递归保存当前集合的状态(异或和),选择将当前元素添加至当前状态与否,并依次递归数组中下一个元素。当递归到空元素时,表示所有元素都被考虑到,记录当前状态(将当前状态的异或和添加至答案中)。

递归函数设计:void dfs(int val, int idx, vector<int>& nums)

  • 参数:val(当前状态的异或和),idx(当前需要处理的元素下标,处理过程:选择将其添加至当前状态或不进行操作);
  • 返回值:无;
  • 函数作用:选择对元素进行添加与否处理。

递归流程:

1. 递归结束条件:当前下标与数组长度相等,即已经越界,表示已经考虑到所有元素;

  • 将当前异或和添加至答案中,并返回;

2. 考虑将当前元素添加至当前状态,当前状态更新为与当前元素值的异或和,然后递归下一个元素;

3. 考虑不选择当前元素,当前状态不变,直接递归下一个元素。

🌵算法代码:

class Solution 
{
    int path;
    int sum;
public:
    int subsetXORSum(vector<int>& nums) 
    {
        dfs(nums, 0);
        return sum;
    }
    void dfs(vector<int>& nums, int pos)
    {
        sum += path;
        for(int i = pos; i < nums.size(); i++)
        {
            path ^= nums[i];
            dfs(nums, i + 1);
            path ^= nums[i];
        }
    }
};

二、全排列 II

1.题目链接:47. 全排列 II

2.题目描述:

给定一个可包含重复数字的序列 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 <= nums.length <= 8
  • -10 <= nums[i] <= 10

3.解法

🌴算法思路:

        因为题目不要求返回的排列顺序,因此我们可以对初始状态排序,将所有相同的元素放在各自相邻的位置,方便之后操作。因为重复元素的存在,我们在选择元素进行全排列时,可能会存在重复排列,例如:[1, 2, 1],所有的 下标排列 为:

123
132
213
231
312
321

按照以上下标进行排列的结果为:

121
112
211
211
112
121

可以看到,有效排列只有三种[1, 1, 2],[1, 2, 1],[2, 1, 1],其中每个排列都出现两次。因此,我们需要对相同元素定义⼀种规则,使得其组成的排列不会形成重复的情况:

  1. 我们可以将相同的元素按照排序后的下标顺序出现在排列中,通俗来讲,若元素 s 出现 x 次,则排序后的第 2 个元素 s ⼀定出现在第 1 个元素 s 后面,排序后的第 3 个元素 s 一定出现在第 2 个元素 s 后面,以此类推,此时的全排列⼀定不会出现重复结果。
  2. 例如:a1=1,a2=1,a3=2,排列结果为 [1, 1, 2] 的情况只有⼀次,即 a1 在 a2 前面,因为 a2 不会出现在 a1 前面从而避免了重复排列。
  3. 我们在每⼀个位置上考虑所有的可能情况并且不出现重复;
  4. *注意*:若当前元素的前⼀个相同元素未出现在当前状态中,则当前元素也不能直接放入当前状态的数组,此做法可以保证相同元素的排列顺序与排序后的相同元素的顺序相同,即避免了重复排列出现。
  5. 通过深度优先搜索的方式,不断地枚举每个数在当前位置的可能性,并在递归结束时回溯到上⼀个状态,直到枚举完所有可能性,得到正确的结果。

递归函数设计:void backtrack(vector<int>& nums, int idx)

  • 参数:idx(当前需要填入的位置);
  • 返回值:无;
  • 函数作用:查找所有合理的排列并存储在答案列表中。

递归流程如下:

1. 定义⼀个二维数组 ans 用来存放所有可能的排列,⼀个⼀维数组 perm ⽤来存放每个状态的排列,⼀个⼀维数组 visited 标记元素,然后从第⼀个位置开始进行递归;

2. 在每个递归的状态中,我们维护⼀个步数 idx,表示当前已经处理了几个数字;

3. 递归结束条件:当 idx 等于 nums 数组的长度时,说明我们已经处理完了所有数字,将当前数组存入结果中;

4. 在每个递归状态中,枚举所有下标 i,若这个下标未被标记,并且在它之前的相同元素被标记过,则使用 nums 数组中当前下标的元素:

  • 将 visited[i] 标记为 1;
  • 将 nums[i] 添加至 perm 数组末尾;
  • 对第 step+1 个位置进行递归;
  • 将 visited[i] 重新赋值为 0,并删除 perm 末尾元素表示回溯;

5. 最后,返回 ans。

🌴算法代码:

class Solution 
{
    vector<int> path;
    vector<vector<int>> ret;
    bool check[9];

public:
    vector<vector<int>> permuteUnique(vector<int>& nums) 
    {
        sort(nums.begin(), nums.end());
        dfs(nums, 0);
        return ret;
    }
    void dfs(vector<int>& nums, int pos) 
    {
        if (pos == nums.size()) 
        {
            ret.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++) 
        {
            // 剪枝
            if (check[i] == false && (i == 0 || nums[i] != nums[i - 1] || check[i - 1] != false)) 
            {
                path.push_back(nums[i]);
                check[i] = true;
                dfs(nums, pos + 1);
                path.pop_back(); // 恢复现场
                check[i] = false;
            }
        }
    }
};

三、电话号码的字母组合

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

2.题目描述:

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

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

示例 1:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

示例 2:

输入:digits = ""
输出:[]

示例 3:

输入:digits = "2"
输出:["a","b","c"]

3.解法

🍓算法思路:

        每个位置可选择的字符与其他位置并不冲突,因此不需要标记已经出现的字符,只需要将每个数字对应的字符依次填入字符串中进行递归,在回溯时撤销填入操作即可。

  • 在递归之前我们需要定义一个字典 phoneMap,记录 2~9 各自对应的字符。

递归函数设计:void backtrack(unordered_map<char, string>& phoneMap, string& digits, int index)

  • 参数:index (已经处理的元素个数),ans (字符串当前状态),res (所有成立的字符串);
  • 返回值:无。
  • 函数作用:查找所有合理的字母组合并存储在答案列表中。

递归函数流程如下:

  1. 递归结束条件:当 index 等于 digits 的长度时,将 ans 加入到 res 中并返回;
  2. 取出当前处理的数字 digit,根据 phoneMap 取出对应的字母列表 letters;
  3. 遍历字母列表 letters,将当前字母加⼊到组合字符串 ans 的末尾,然后递归处理下一个数字(传入 index + 1,表示处理下一个数字);
  4. 递归处理结束后,将加入的字母从 ans 的末尾删除,表示回溯。
  5. 最终返回 res 即可。

🍓算法代码:

class Solution 
{
    string hash[10] = {" ", " ", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    string path;
    vector<string> ret;
public:
    vector<string> letterCombinations(string digits) 
    {
        if (0 == digits.size()) return ret;
        dfs(digits, 0);
        return ret;
    }
    void dfs(string& digits, int pos)
    {
        if(pos == digits.size())
        {
            ret.push_back(path);
            return;
        }
        for(auto ch : hash[digits[pos] - '0'])
        {
            path.push_back(ch);
            dfs(digits, pos + 1);
            path.pop_back();// 恢复现场
        }
    }
};

四、括号生成

1.题目链接:22. 括号生成

2.题目描述:

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]

示例 2:

输入:n = 1
输出:["()"]

3.解法

🍍算法思路:

        从左往右进行递归,在每个位置判断放置左右括号的可能性,若此时放置左括号合理,则放置左括号继续进行递归,右括号同理。

        一种判断括号是否合法的方法:从左往右遍历,左括号的数量始终大于等于右括号的数量,并且左括号的总数量与右括号的总数量相等。因此我们在递归时需要进行以下判断:

  1. 放入左括号时需判断此时左括号数量是否小于字符串总长度的一半(若左括号的数量大于等于字符串长度的一半时继续放置左括号,则左括号的总数量一定大于右括号的总数量);
  2. 放入右括号时需判断此时右括号数量是否小于左括号数量。

递归函数设计:void dfs(int step, int left)

  • 参数:step(当前需要填入的位置),left(当前状态的字符串中的左括号数量);
  • 返回值:无;
  • 函数作用:查找所有合理的括号序列并存储在答案列表中。

递归函数参数设置为当前状态的字符串长度以及当前状态的左括号数量,递归流程如下

  1. 递归结束条件:当前状态字符串长度与 2*n 相等,记录当前状态并返回;
  2. 若此时左括号数量小于字符串总长度的一半,则在当前状态的字符串末尾添加左括号并继续递归,递归结束撤销添加操作;
  3. 若此时右括号数量小于左括号数量(右括号数量可以由当前状态的字符串长度减去左括号数量求得),则在当前状态的字符串末尾添加右括号并递归,递归结束撤销添加操作;

🍍算法代码:

class Solution 
{
    int left, right, n;
    string path;
    vector<string> ret;
public:
    vector<string> generateParenthesis(int _n) 
    {
        n = _n;
        dfs();
        return ret;
    }
    void dfs()
    {
        if (right == n)
        {
            ret.push_back(path);
            return;
        }
        // 添加左括号
        if (left < n)
        {
            path.push_back('('); left++;
            dfs();
            path.pop_back(); left--;// 恢复现场
        }
        // 添加右括号
        if (right < left)
        {
            path.push_back(')'); right++;
            dfs();
            path.pop_back(); right--;// 恢复现场
        }
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2033563.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

“低代码技术:数字化工厂的加速器与智能制造的桥梁“

引言 随着工业4.0和智能制造的快速发展&#xff0c;数字化工厂成为了现代制造业的核心概念。这一转型不仅依赖于物联网、人工智能、大数据等新兴技术&#xff0c;也需要软件开发的支持。然而&#xff0c;传统的软件开发模式由于其复杂性和周期长&#xff0c;难以快速适应数字化…

案例-华东某财险公司:引入监控易一体化运维软件

在华东这片经济活跃、金融繁荣的热土上&#xff0c;某财险公司作为行业内的佼佼者&#xff0c;始终致力于通过科技创新提升业务运营效率与服务品质。随着公司业务规模的不断扩大&#xff0c;IT基础设施的复杂性和重要性日益凸显&#xff0c;如何实现对庞大IT基础资源的实时监控…

从概念到现实:手机设计可视化如何重塑科技美学

在这个日新月异的数字时代&#xff0c;手机已不仅仅是通讯工具&#xff0c;它更是我们生活方式的延伸&#xff0c;是连接世界的窗口&#xff0c;是创意与科技的完美结晶。 想象一下&#xff0c;在浩瀚的数字海洋中&#xff0c;一款尚未面世的手机&#xff0c;通过高精度的3D建模…

在亚马逊云科技上安全、合规、私密地调用生成式AI大模型

项目简介&#xff1a; 小李哥将继续每天介绍一个基于亚马逊云科技AWS云计算平台的全球前沿AI技术解决方案&#xff0c;帮助大家快速了解国际上最热门的云计算平台亚马逊云科技AWS AI最佳实践&#xff0c;并应用到自己的日常工作里。 本次介绍的是如何安全、合规、私密地调用亚…

2024最全最新VMWare以及Linux配置(含yum失效解决方案)

血泪教训浓缩的精华配置、报错解决&#xff08;解决99%问题&#xff09; 目录 1.Linux环境搭建 1.1安装VMWare 1.1.1卸载老版本VMWare&#xff08;如果有的话&#xff09; 1.1.2开始安装VMware 1.2创建虚拟机 1.3安装Centos7 1.4设置虚拟机快照 1.5安装远程连接SSH客户…

AI换脸模型(384-448模型430万底丹)

模型训练素材19万张来自于 1、香港中文大学CelebA预训练集-WF512版-量大角度全-11万5千张 2、DST全角度训练图集V3.1 WF512【2.6W张 6GB 】 3、女性人脸数据&#xff0c;预训练炼丹专用 4、补全SRC极限角度 5、全角度512分辨率7万张素材 下载地址&#xff1a; 链接&#xf…

day09——集合ArrayList

ArrayList类 ArrayList表示一种集合&#xff0c;它是一个容器&#xff0c;用来存储数据的&#xff0c;类似于数组。但不同于数组&#xff0c;数组一旦创建大小不变&#xff0c;而集合大小是可变的。 ArrayList常用方法 ArrayList是泛型类&#xff0c;可以约束存储的数据类型…

TLE8386-2EL:汽车级DC-DC转换器中文资料书

描述 TLE8386-2EL是一款具有内置保护功能的低端感应升压控制器。该器件的主要功能是将输入电压升高&#xff08;升压&#xff09;到更大的输出电压。开关频率可从100kHz调整至700kHz&#xff0c;并可与外部时钟源同步。 TLE8386-2EL的独特功能可将关断电流消耗降至 <2μA。该…

Python 如何进行静态代码分析(pylint, flake8)

Python 的静态代码分析是一种在不执行代码的情况下&#xff0c;对代码进行检查的技术。其主要目的是在代码运行前发现潜在的错误、风格问题以及其他代码质量问题。静态代码分析在提高代码的可维护性、可读性和可靠性方面发挥着重要作用。常用的静态代码分析工具包括 Pylint 和 …

观测云突变告警,精准预测云原生的系统异常

背景 观测云 DataKit 是一个强大的数据采集工具&#xff0c;能够收集和监控容器化环境和 Kubernetes 集群的指标、对象和日志数据。通过灵活使用 DataKit 收集的数据&#xff0c;可以对 Kubernetes 集群进行深入的监控和分析&#xff0c;从而实现更好的运维和优化。以下是一些…

如何使用yolov5-master进行训练

主要记录配置完成的yolov5-master如何进行自定义数据集训练。 本人使用的设备是jetson-orin AGX&#xff0c;系统是英伟达官方魔改过的ubuntu18.04.。 一、修改配置 首先观察train.py的__main__部分&#xff0c;我们发现训练程序接受很多个参数。 其中我们必须要关注的参数包括…

【网络编程】UDP实现网络通信(C语言、Ubuntu实现)

代码实现&#xff1a;分为udpSer.c(服务器端)和udpCli.c(客户端) //udpSer.c #include <myhead.h>#define SER_PORT 9999 #define SER_IP "192.168.83.128"int main(int argc, const char *argv[]) {//1、创建用于通信的服务器套接字文件描述符int sfd sock…

AI大模型学习之白话笔记(三) -- Assistants API入门

经过上面两篇文章的学习&#xff0c;恭喜你开始进入了人工智能的世界&#xff01;在这篇文章中&#xff0c;你将跟我一起学习如何使用Assistants API快速构建一个针对特定领域的人工智能助手。 我们从基础开始&#xff0c;一步步带你用Assistants API完成一个应用的整个过程。…

智能分析/视频汇聚EasyCVR安防视频融合管理云平台技术优势分析

安防行业的发展历程主要围绕视频监控技术的不断改革升级&#xff0c;从最初的模拟监控到数字监控&#xff0c;再到高清化、网络化监控&#xff0c;直至现在的智能化监控&#xff0c;每一次变革都推动了行业的快速发展。特别是近年来&#xff0c;随着AI、大数据、物联网等技术的…

Proxmox LXC 部署ubuntu,centos

前言CT 在 Proxmox VE 中,“CT”是 Container(容器)的缩写。PVE 支持两种主要类型的容器:LXC 和 OpenVZ(针对较旧的 Linux 内核)。这些容器提供了一种轻量级的虚拟化方式,允许用户在一个操作系统实例上运行多个隔离的系统环境。 PVE CT 特别是指在 Proxmox 平台上创建…

深入探索MySQL C API:使用C语言操作MySQL数据库

目录 引言 一. MySQL C API简介 二. MySQL C API核心函数 2.1 初始化和连接 2.2 配置和执行 2.3 处理结果 2.4 清理和关闭 2.5 错误处理 三. MySQL使用过程 四. 实现CRUD操作 4.1 创建数据库并建立表 ​编辑 4.2 添加数据&#xff08;Create&#xff09; ​编辑 …

idea git 重新登录

&#xff08;切换本地项目绑定的git账户&#xff09; git config --system --unset credential.helper git config user.name git pull &#xff08;然后输入用户名&#xff08;输绑定邮箱&#xff09;&#xff0c;密码&#xff09;

easyExcel2.1.6自动trim()的问题

环境&#xff1a;easyExcel 2.1.6 问题&#xff1a;easyExcel会自动忽略String中的空格&#xff0c;调用trim()函数&#xff0c;导致excel中的空格失效。 代码如上所示&#xff0c;所以只需要把globalConfiguration的autoTrim()&#xff0c;设置为false即可 那么怎么设置confi…

stable diffusion学习(1)成功打开WEBUI

建议学习github上的资源 GitHub - AUTOMATIC1111/stable-diffusion-webui: Stable Diffusion web UI 1.下载安装sd.webui.zip 1.1 Release v1.0.0-pre AUTOMATIC1111/stable-diffusion-webui GitHub 1.2安装python&#xff0c;设置阿里镜像 python -m pip config set gl…

Linux桥转发经过的netfilter钩子点

你是否也有这样的疑问&#xff0c;当我们路由器局域内的下挂设备&#xff0c;是怎么互相访问的呢。假设我的路由器当前的网段是192.168.1.1&#xff0c;有一台PC有线接入&#xff0c;IP地址为 192.168.1.142 &#xff0c;一台手机WiFi接入&#xff0c;IP地址为192.168.1.223&am…