穷举vs暴搜vs深搜vs回溯vs剪枝(一)

文章目录

  • 全排列
  • 子集
  • 找出所有子集的异或总和再求和
  • 全排列 II
  • 电话号码的字母组合

全排列

题目:全排列

在这里插入图片描述

思路

通过深度优先搜索的方式,不断枚举每个数在当前位置的可能性,然后回溯到上一个状态,直到枚举完所有可能性得到正确的结果

在这里插入图片描述

  • res:一个二维向量vector<vector<int>>,用于存储所有生成的排列。
  • path:一个一维向量vector<int>,用于存储当前正在构建的排列。
  • check:一个布尔数组bool check[7],用于标记数组nums中的每个元素是否已经被用于当前排列中。这里假设nums数组的大小不会超过6(因为数组索引从0开始,最大索引为6时,数组大小为7)。
  • 递归的终止条件是path的大小等于nums的大小。
  • 在递归过程中,使用check数组来确保不会重复使用同一个元素。
  • 使用path.push_back(nums[i])path.pop_back()来实现回溯,即在尝试下一个元素之前,需要将当前元素从path中移除,以便尝试其他可能的元素组合。
  • 通过check[i] = truecheck[i] = false来标记元素是否已被使用。

C++代码

class Solution 
{
    vector<vector<int>> res;
    vector<int> path;
    bool check[7];

public:
    void dfs(vector<int>& nums)
    {
        if(nums.size() == path.size())
        {
            res.push_back(path);
            return ;
        }

        for(int i = 0; i < nums.size(); i ++ )
        {
            if(!check[i])
            {
                path.push_back(nums[i]);
                check[i] = true;
                dfs(nums);
                // 回溯 -> 恢复现场
                path.pop_back();
                check[i] = false;
            }
        }
    }

    vector<vector<int>> permute(vector<int>& nums) 
    {
        dfs(nums);
        return res;
    }
};

子集

题目:子集

在这里插入图片描述

思路1

在这里插入图片描述

我们先将所有结果个数为1的选出来,再再其基础上选出结果个数为2的,依次类推

  • pos 开始遍历 nums 数组。对于每个位置 i ,执行以下操作:
    nums[i] 添加到 path 中。
  • 递归调用 dfs 函数,以 i + 1 作为新的起始位置,继续搜索。
  • 在递归调用返回后,进行回溯操作:将刚刚添加到 path 中的 nums[i] 移除,以便尝试其他可能的元素组合(或停止进一步搜索)。

C++代码

class Solution 
{
    vector<vector<int>> ret;
    vector<int> path;
public:
    vector<vector<int>> subsets(vector<int>& nums) 
    {
        dfs(nums, 0);
        return ret;
    }

    void dfs(vector<int>& nums, int pos)
    {
        ret.push_back(path);

        for(int i = pos; i < nums.size(); i++)
        {
            path.push_back(nums[i]);
            dfs(nums, i + 1);
            path.pop_back();
        }
    }
};

思路2

在这里插入图片描述

依次枚举,1选不选,选的话在1基础上2选不选,选的话在2基础上选不选,枚举出所有结果;当当前位置等于数组大小时,将结果加入答案中

  • 首先检查pos是否等于nums的大小。如果是,说明已经遍历到数组的末尾,此时path代表了一个完整的子集(可能是空集,也可能是包含数组所有元素的集合,这取决于dfs的调用过程)。然后,将这个子集添加到ret中,并返回
  • nums[pos]添加到path中,然后递归调用dfs函数,以pos + 1作为新的起始位置继续搜索。在递归调用返回后,执行回溯操作:从path中移除nums[pos],以便尝试其他可能的元素组合或停止进一步搜索。
class Solution 
{
    vector<vector<int>> ret;
    vector<int> path;
public:
    vector<vector<int>> subsets(vector<int>& nums) 
    {
        dfs(nums, 0);
        return ret;
    }

    void dfs(vector<int>& nums, int pos) 
    {
        if (pos == nums.size()) 
        {
            ret.push_back(path);
            return;
        }

        path.push_back(nums[pos]);
        dfs(nums, pos + 1);
        path.pop_back();

        dfs(nums, pos + 1);
    }
};

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

题目:找出所有子集的异或总和再求和

在这里插入图片描述
思路

本题和上题思路一样,我们使用上题的第一种思路,依次枚举,1选不选,选的话在1基础上2选不选,选的话在2基础上选不选;
不同的是将每个子集的值异或,并将其相加

  • 从数组的第一个元素开始,递归地构建所有可能的子集
  • 在每个递归步骤中,可以选择包含当前元素(通过异或操作更新path)或不包含当前元素(直接递归到下一个位置)
  • int path选择1将其异或在path上,再选2异或在path上;
  • 当到达数组的末尾时,将当前的 path(即当前子集的异或和)加到 res

C++代码

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

    }
    int subsetXORSum(vector<int>& nums) 
    {
        dfs(nums, 0);
        return res;
    }
};

全排列 II

题目:全排列 II

在这里插入图片描述
思路

同一个节点的分支中,相同的元素只能选择一次
同一个数只能使用一次


  • 只关心不合法分支

if(cheak[i] == true) || (i != 0 && nums[i] == nums[i-1] && cheak[i-1] == false))

C++代码

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] == true|| (i != 0 && nums[i] == nums[i - 1] && check[i - 1] == false)) 
                continue;
            path.push_back(nums[i]);
            check[i] = true;
            dfs(nums, pos + 1);
            path.pop_back();
            check[i] = false;
        }

    }
};
  • 只关心合法分支

if(cheak[i] == false) && (i == 0 || nums[i] != nums[i-1] ||cheak[i-1] != false))

C++代码

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;
            }
        }

    }
};

电话号码的字母组合

题目:电话号码的字母组合

在这里插入图片描述
思路

利用一个全局的字符串数组来帮我映射数组和字母之间的关系

  • 如果pos等于digits的长度,说明已经处理完所有数字,将当前的path(即一个完整的字母组合)添加到结果数组ret中,并返回
  • 否则,对于当前数字digits[pos],遍历其对应的所有字母(通过str[digits[pos] - '0']访问。对于每个字母,执行以下操作
    • 将当前字母添加到path的末尾。
    • 递归调用dfs函数,处理下一个数字pos + 1
    • 在递归返回后,将刚刚添加的字母从path中移除,以便尝试当前数字对应的下一个字母。

C++代码

class Solution 
{
    vector<string> ret;
    string path;
    string str[10]={"",
        "", "abc", "def",
        "ghi", "jkl", "mno",
        "pqrs", "tuv", "wxyz"
    };
public:
    vector<string> letterCombinations(string digits) 
    {
        if(digits.size() == 0) 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 : str[digits[pos] - '0'])
        {
            path.push_back(ch);
            dfs(digits, pos + 1);
            path.pop_back();
        }
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SDK5(note上)

以下代码实现菜单窗口和快捷键的ctrlo打开的操作 #include <windows.h> #include<tchar.h> #include <stdio.h> #include <strsafe.h> #include <string> #define IDM_OPEN 102 /*鼠标消息 * 键盘消息 * Onkeydown * Onkeyup * //键盘扫描码 * …

力扣hot100--二叉树

目录 二叉树 1. 94. 二叉树的中序遍历 2. 98. 验证二叉搜索树 3. 101. 对称二叉树 4.102. 二叉树的层序遍历 5. 104. 二叉树的最大深度 6. 105. 从前序与中序遍历序列构造二叉树 7. 114. 二叉树展开为链表 8. 226. 翻转二叉树 9. 236. 二叉树的最近公共祖先 二叉树 …

操作符详解(C 语言)

目录 一、操作符的分类二、算数操作符1. 除法操作符2. 取余操作符 三、位移操作符1. 进制2. 原码、反码和补码3. 左移操作符&#xff08;<<&#xff09;和右移操作符&#xff08;>>&#xff09; 四、位操作符1. 按位与 &2. 按位或 |3. 按位异或 ^4. 按位取反 ~…

大衍数列——考研408考试科目之数据算法——未来之窗学习通

一、大衍数列 中国古代文献中&#xff0c;曾记载过“大衍数列”, 主要用于解释中国传统文化中的太极衍生原理。 它的前几项是&#xff1a;0、2、4、8、12、18、24、32、40、50 … 其规律是&#xff1a;对偶数项&#xff0c;是序号平方再除2&#xff0c;奇数项&#xff0c;是…

有源滤波器(三)

这个连接方法很可以&#xff0c;正好解决了最近没有转接器的问题&#xff1a;

javaweb-xml映射文件编写sql语句

可以使用注解的方式&#xff0c;也可以使用xml映射的方式&#xff0c;一般简单sql语句使用注解&#xff0c;复杂的使用xml映射。

【PhpSpreadsheet】ThinkPHP5+PhpSpreadsheet实现批量导出数据

目录 前言 一、安装 二、API使用 三、完整实例 四、效果图 前言 为什么使用PhpSpreadsheet&#xff1f; 由于PHPExcel不再维护&#xff0c;所以建议使用PhpSpreadsheet来导出exlcel&#xff0c;但是PhpSpreadsheet由于是个新的类库&#xff0c;所以只支持PHP7.1及以上的版…

【文心智能体 | AI大师工坊】如何使用智能体插件,完成一款购物类智能体的开发,来体验一下我的智能体『科技君Tom』

目录 1.1、智能体运行效果1.2、创作灵感来源智能体平台拥有个人化且人性化的大致框架&#xff0c;可以让小白也能搭建出一个智能体其次是拥有丰富的插件&#xff0c;可以更加快速的得到自己想要的效果~ 1.3、如何制作智能体常见问题与解决方案关于人设与回复逻辑插件使用模型的…

【黑马redis高级篇】持久化

//来源[01,05]分布式缓存 除了黑马&#xff0c;还参考了别的。 目录 1.单点redis问题及解决方案2.为什么需要持久化&#xff1f;3.Redis持久化有哪些方式呢&#xff1f;为什么我们需要重点学RDB和AOF&#xff1f;4.RDB4.1 定义4.2 触发方式4.2.1手动触发save4.2.2被动触发bgsa…

文件IO(Linux文件IO,目录操作函数)

前言 本文介绍Linux系统下自带的文件IO的函数。 Linux文件IO相关函数 open函数 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode)…

2024ideaUI切换和svn与git的切换

2024的UI实在很不舒服&#xff0c;隐藏了很多按键&#xff1b; 第一步&#xff1a; 视图 -》 外观 -》 工具栏选出来&#xff1b; 结果出来&#xff1a; 运行的按键和设置的按钮 第二步 点击设置的按钮&#xff0c;选择最后一个&#xff0c;重启就行 结果 舒服&#xff01; s…

双通道音频功率放大电路D2822M兼容TDA2822,全封装输出功率0.11W,用于音频产品

在某客户的便携式音频产品中&#xff0c;客户想在确保其产品的性能的前提下&#xff0c;为产品方案寻找一颗国产备份料。客户产品之前使用的是TDA2822&#xff0c;在了解客户的电路设计以及该产品的电气特性后&#xff0c;给客户寻找了一款可兼容相同电路设计使用的国产厂牌芯谷…

2010年国赛高教杯数学建模C题输油管的布置解题全过程文档及程序

2010年国赛高教杯数学建模 C题 输油管的布置 某油田计划在铁路线一侧建造两家炼油厂&#xff0c;同时在铁路线上增建一个车站&#xff0c;用来运送成品油。由于这种模式具有一定的普遍性&#xff0c;油田设计院希望建立管线建设费用最省的一般数学模型与方法。   1. 针对两炼…

Python零基础01——Python的由来,Python的特点、优缺点有哪些?

文章目录 Python的由来Python的特点Python的优缺点什么是编译器 Python的由来 1989年圣诞节期间&#xff0c;在阿姆斯特丹为打发圣诞节的无趣&#xff0c;决定开发一款新的脚本解释语言&#xff0c;作为ABC语言的一种继承&#xff0c;然后他就这么做了&#xff0c;并实现了&am…

Python中的help()函数:追踪错误并提供解决方案

引言 在Python编程中&#xff0c;help()函数是一个非常有用的内置工具&#xff0c;它能够提供关于模块、关键字、属性和方法等的详细信息。对于初学者来说&#xff0c;help()函数可以帮助他们理解代码的工作原理&#xff0c;快速查找文档&#xff0c;以及解决编程过程中遇到的…

<Linux> 线程安全

目录 文章目录 一、Linux线程互斥 1. 进程线程间的互斥相关背景概念 2. 互斥量mutex 3. 互斥量接口 初始化互斥量 动静态分配 销毁互斥量 互斥量加锁 互斥量解锁 4. 互斥量实现原理 5. 简单封装互斥量 二、可重入与线程安全 1. 概念 1.1 可重入 1.2 线程安全 2. 常见的线程不…

LLama 3.2 1B 和 3B:体型虽小,但威力强大!

MetaAI 刚刚推出了 Llama-3.2&#xff0c;这是一套新的模型&#xff0c;其中包括两个令人印象深刻的轻量级大型语言模型 (LLM)&#xff0c;分别具有 10 亿 (1B) 和 30 亿 (3B) 个参数&#xff0c;以及更大的视觉语言模型 (VLM)&#xff0c;分别具有 11B 和 90B 个参数。 取决于…

IT监控平台可视化:多维度展示助力运维效率提升

在信息化时代&#xff0c;IT设备的稳定性与业务的连续性紧密相连&#xff0c;任何细微的故障都可能给企业带来巨大的损失。因此&#xff0c;IT运维团队面临着前所未有的挑战&#xff0c;他们需要迅速、准确地识别和解决问题&#xff0c;以确保业务的平稳运行。而IT监控平台的可…

Django学习- ORM基础操作_创建数据

ORM操作&#xff1a; 管理器对象&#xff1a; 创建数据&#xff1a; Django shell 想要操作模型对象&#xff0c;首先我们需要把它引进Django shell中 >>> from bookstore.models import Book >>> b1 Book.objects.create(titleAI, pub清华大学出版社, pr…

Java | Leetcode Java题解之第478题在圆内随机生成点

题目&#xff1a; 题解&#xff1a; class Solution {Random random;double xc, yc, r;public Solution(double radius, double x_center, double y_center) {random new Random();xc x_center;yc y_center;r radius;}public double[] randPoint() {double u random.next…