【算法系列篇】递归、搜索和回溯(四)

在这里插入图片描述

文章目录

  • 前言
  • 什么是决策树
  • 1. 全排列
    • 1.1 题目要求
    • 1.2 做题思路
    • 1.3 代码实现
  • 2. 子集
    • 2.1 题目要求
    • 2.2 做题思路
    • 2.3 代码实现
  • 3. 找出所有子集的异或总和再求和
    • 3.1 题目要求
    • 3.2 做题思路
    • 3.3 代码实现
  • 4. 全排列II
    • 4.1 题目要求
    • 4.2 做题思路
    • 4.3 代码实现

前言

前面我们通过几个题目基本了解了解决递归类问题的基本思路和步骤,相信大家对于递归多多少少有了更加深入的了解。那么本篇文章我将为大家分享结合决策树来解决递归、搜索和回溯相关的问题。

什么是决策树

决策树是一种基本的分类与回归方法。在分类问题中,决策树通过构建一棵树形图来对数据进行分类。树的每个节点表示一个特征属性,每个分支代表一个特征属性上的判断条件,每个叶节点代表一个类别。在回归问题中,决策树可以预测一个实数值。

下面是一个简单的决策树:
在这里插入图片描述
知道了什么是决策树,下面我们将运用决策树来解决实际问题。

1. 全排列

https://leetcode.cn/problems/permutations/

1.1 题目要求

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

示例 1:

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

示例 2:

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

示例 3:

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

提示:

1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同
class Solution {
    public List<List<Integer>> permute(int[] nums) {

    }
}

1.2 做题思路

相信大家肯定做过跟排列相关的问题,就是三个人坐座位的问题。第一座位可以坐A、B、C 任何一个人,如果第一个座位坐的是 A 的话,那么第二个位子 A 就不能再坐了,第二个位子就只能在 B、C 之间选择了,如果 B 选择了第二个位子,那么第三个位置就只能 C 选择了。所以这个问题通过决策树来体现的话就是这样的:

在这里插入图片描述
但是上面的图我们会发现这几种情况会有重复的情况,那么我们如何筛选掉这些重复的情况呢?可以使用一个标记数组来记录已经选择过的元素,当下一次选择的时候就选择这个标记数组中没有被选择的剩下的元素的其中一个。这道题目跟上面的例子的思路是一样的,这里我就不为大家再画一个图了。

那么这道题使用代码的思想该如何解决呢?每次递归我们还是将数组中的所有元素都给列举出来,不过我们需要根据标记数组中元素的使用情况来选择是否可以选择这个元素,如果某个元素没有被选择,那么这次就选择这个元素,将这个元素标记为已使用,然后继续递归,当当前情况列举完成之后就需要恢复现场,当路径集合中记录的元素的个数和数组中的元素个数相同的时候,就说明一种情况已经列举完成,就可以将当前情况添加进ret集合中,返回。

1.3 代码实现

class Solution {
    List<Integer> path;
    List<List<Integer>> ret;
    boolean[] vis;
    public List<List<Integer>> permute(int[] nums) {
        //对全局变量进行初始化
        path = new ArrayList<>();
        ret = new ArrayList<>();
        vis = new boolean[nums.length];
        dfs(nums);
        return ret;
    }

    private void dfs(int[] nums) {
    	//当path中元素的大小等于数组的大小,就说明一种情况已经列举完成,这事需要我们将当前path中的数据添加进ret中,并且返回
        if (path.size() == nums.length) {
            ret.add(new ArrayList<>(path));
            return;
        }
       for (int i = 0; i < nums.length; i++) {
           if (vis[i] == false) {
               path.add(nums[i]);
               //将当前元素标记为已使用
               vis[i] = true;
               //考虑该位置之后的其他元素的选择
               dfs(nums);
               //恢复现场
               path.remove(path.size() - 1);
               vis[i] = false;
           }
       }
    }
}

在这里插入图片描述

2. 子集

https://leetcode.cn/problems/subsets/

2.1 题目要求

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

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

示例 1:

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

示例 2:

输入:nums = [0]
输出:[[],[0]]

提示:

1 <= nums.length <= 10
-10 <= nums[i] <= 10
nums 中的所有元素 互不相同
class Solution {
    public List<List<Integer>> subsets(int[] nums) {

    }
}

2.2 做题思路

前面全排列中是当路径集合中的元素个数和数组中的元素的个数相同的时候视为一种情况,这道题目就不一样了,这个是数组的子集,也就是说每一种情况的元素的个数可能是不一样的,所以我们路径集合每新添加一个元素就可以视为一种情况,就需要将路径中的元素添加进ret集合中,思路跟上一道题目是类似的,都是通过决策树递归来实现的,但是呢?仔细看题目可以发现,就是集合[1,2],[2,1]是一种情况,也就是说子集的选择跟顺序无关,那么我们又该如何避免出现重复的情况呢?

这其实也不难,想想如果是在数学中我们会怎样思考?如果当前位置我们选择了某个元素,那么后面的位置我们就从这个元素的后面元素中去选择。

在这里插入图片描述
所以通过代码体现的话,就是我们可以使用一个 pos 变量来记录当前位置选择的元素的下标,然后下一个位置选择元素递归的话,我们就从 pos 的下一个位置开始选择。

2.3 代码实现

class Solution {
    List<Integer> path;
    List<List<Integer>> ret;
    public List<List<Integer>> subsets(int[] nums) {
        path = new ArrayList<>();
        ret = new ArrayList<>();
        dfs(nums, 0)
        return ret;
    }

    private void dfs(int[] nums, int pos) {
        //进入这个函数就可以将path中的结果添加进ret中,这样就可以将空集的情况给考虑上
        ret.add(new ArrayList<>(path));
        //循环的话,就从pos位置开始遍历
        for (int i = pos; i < nums.length; i++) {
            path.add(nums[i]);
            dfs(nums, i + 1);
            path.remove(path.size() - 1);
        }
    }
}

在这里插入图片描述

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

https://leetcode.cn/problems/sum-of-all-subset-xor-totals/

3.1 题目要求

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

提示:

1 <= nums.length <= 12
1 <= nums[i] <= 20
class Solution {
    public int subsetXORSum(int[] nums) {

    }
}

3.2 做题思路

这道题目跟上面的子集思路基本上没什么区别,之不过上面的子集是要求出所有子集的情况,而这道题目是求出所有子集异或之后的总和。因为思路基本跟上个题一样,所以我们直接来看代码。

3.3 代码实现

class Solution {
    int path;
    int ret;
    public int subsetXORSum(int[] nums) {
        dfs(nums, 0);
        return ret;
    }

    private void dfs(int[] nums, int pos) {
        //前面是将集合添加进ret中,这里我们是将每种情况加进ret中
        ret += path;
        for (int i = pos; i < nums.length; i++) {
            //这里我们不是将新加入的元素加入到path集合中,而是将新加入的元素和之前path元素的异或的结果异或
            path ^= nums[i];
            dfs(nums, i + 1);
            //恢复现场(两个相同的元素异或,结果为0)
            path ^= nums[i];
        }
    }
}

在这里插入图片描述

4. 全排列II

https://leetcode.cn/problems/permutations-ii/

4.1 题目要求

给定一个可包含重复数字的序列 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
class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {

    }
}

4.2 做题思路

这道题目跟 全排列I 是不一样的,全排列I 中不存在重复的元素,但是这道题目中存在重复的元素,也就是说[1, 1, 2] 和 [1, 1, 2] 是同一个排列,这不看起来就是同一个排列吗?难道还能不同吗?其实这里的 1 不是同一个1,[1(下标为0), 1(下标为1), 2],[1(下标为1), 1(下标为0), 2],全排列I 中我们只需要使用一个标记数组来避免同一个元素被重复使用的情况,而这个 全排列II 中,我们还需要筛选出因元素相同而导致的相同排列的情况。那么如何筛选呢?我们来看个例子:

在这里插入图片描述

4.3 代码实现

class Solution {
    List<Integer> path;
    List<List<Integer>> ret;
    boolean[] vis;
    public List<List<Integer>> permuteUnique(int[] nums) {
        path = new ArrayList<>();
        ret = new ArrayList<>();
        vis = new boolean[nums.length];
        //首先将重复元素给排序到一起
        Arrays.sort(nums);
        dfs(nums);
        return ret;
    }

    private void dfs(int[] nums) {
        if (path.size() == nums.length) {
            ret.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (vis[i] == false && (i == 0 || (nums[i - 1] != nums[i]) || vis[i - 1] == true)) {
                path.add(nums[i]);
                vis[i] = true;
                dfs(nums);
                //恢复现场
                path.remove(path.size() - 1);
                vis[i] = false;
            }
        }
    }
}

在这里插入图片描述

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

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

相关文章

idea恢复默认出厂设置

idea恢复默认出厂设置 1、IDEA 2021 之后&#xff0c; 在顶部工具栏&#xff0c;选择 File | Manage IDE Settings | Restore Default Settings. 2、或者双击shift搜索Restore Default settings然后点击restore and restart

企业微信无法给Gmail发邮件问题

问题说明 在使用企业微信给国外客户的Gmail邮箱发信件的时候&#xff0c;邮件一直被退信&#xff0c;退信内容如下&#xff1a; 发件人&#xff08;*******.cn&#xff09;域名的DNS记录未设置或设置错误导致对方拒收此邮件。 host gmail-smtp-in.l.google.com[142.251.175.2…

左右按钮实现滚动轮播Demo(js手搓版本)

提示&#xff1a;适用于当放置按钮空间区域有限&#xff0c;通过左右箭头实现有限空间放置更多的按钮的情形&#xff0c;自适应布局的简单Demo支持二次开发和改造 文章目录 效果图Demo源码解释说明总结 效果图 在该区域存在五个按钮&#xff0c;点击左边按钮向左边滚动&#xf…

查看git的帮助信息

说明 在cmd窗口、或者git Bash shell下执行git --help或者git -h命令&#xff0c;可以查看git的帮助信息。 执行git <command> --help命令可以查看某个命令的帮助信息&#xff0c;其中<command>表示某个具体的命令。 示例1&#xff1a;在git Bash shell下运行git…

新能源线束电接头气密测试快速工装

线束气密测试是新能源车生产过程中必须要测试流程&#xff0c;包括常规的电缆测试、电接头测试、接线端子测试等。需要用到相应的快速接头来密封连接线束一端&#xff0c;进行充气或封堵&#xff0c;并连接上检漏仪等相关设备&#xff0c;检查产品密封防水合格性。 线束快速密封…

Vue3 Element Plus自定义年份区间选择组件

环境&#xff1a; "dependencies": {"rollup/plugin-alias": "^3.1.9","types/node": "^17.0.43","element-plus": "^2.2.15","three": "^0.148.0","vue": "^3.2.…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Button按钮组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Button按钮组件 一、操作环境 操作系统: Windows 10 专业版 IDE:DevEco Studio 3.1 SDK:HarmonyOS 3.1 二、Button按钮组件 Button 组件也是基础组件之一&#xff0c;和其它基础组件不…

【深度学习】Prompt

1.Prompt的通俗解释 Prompt就是“提示”的意思&#xff0c;通俗解释可以参考你画我猜游戏。如下图所示&#xff1a;提示词就作为Prompt&#xff0c;指导对方说出正确答案。而自然语言处理任务中的Prompt也有同样的效果&#xff0c;指导模型输出正确的答案。 2.Prompt的不通俗解…

【密码学】群的证明(习题)

0.前置知识 1.习题 记录一次密码学作业~群的判定 2.求解

Linux发行版比较:Ubuntu、CentOS、Red Hat与其他系统的优劣分析

导言 Linux作为开源操作系统&#xff0c;有众多不同的发行版&#xff0c;每个发行版都有其独特的特性和适用场景。本文将聚焦于比较Ubuntu、CentOS、Red Hat和其他系统&#xff0c;深入分析它们的优势、用途以及在不同领域的应用。Linux操作系统的生态系统中&#xff0c;Ubuntu…

传输层—TCP核心机制(确认应答、超时重传、三次握手四次挥手、滑动串口等……)

传输层—TCP核心机制 ​ 文章目录 传输层—TCP核心机制TCP1.1 确认应答机制 (可靠传输机制)1.2 超时重传机制 (可靠传输机制)1.3 连接管理机制 (可靠传输机制)1.3.1 三次握手&#xff08;建立连接&#xff09;1.3.2 四次握手&#xff08;断开连接&#xff09; 1.4 滑动窗口 (提…

如何使用示波器探头对被测电路进行检测

对电路信号进行检测之前首先要知道被测电路是什么电路&#xff0c;被测信号是什么信号。盲目地测试或者使用不正确的测量方法&#xff0c;有可能得到错误的波形甚至损坏仪器危及安全。 1、什么是差分信号&#xff1f;什么是单端信号&#xff1f; 差分传输是一种信号传输的技术…

Selenium自动化测试框架(超详细总结分享)

设计思路 本文整理归纳以往的工作中用到的东西&#xff0c;现汇总成基础测试框架提供分享。 框架采用python3 selenium3 PO yaml ddt unittest等技术编写成基础测试框架&#xff0c;能适应日常测试工作需要。 1、使用Page Object模式将页面定位和业务操作分开&#xff…

SecureCRT for Mac/win强大安全的终端SSH工具,SecureCRT助您网络连接无忧

在当今数字化时代&#xff0c;网络连接已成为生活和工作中不可或缺的一部分。而对于需要进行远程访问和管理的用户来说&#xff0c;一个稳定、安全的终端SSH工具是至关重要的。SecureCRT作为一款强大的终端SSH工具&#xff0c;为用户提供了安全、高效的远程连接解决方案。 首先…

如何压缩视频发邮件?帮你整理了几个必备的!

不同邮件附件上限大小有所不同&#xff0c;QQ邮箱的附件大小限制为2GB&#xff0c;这意味着用户可以发送最大为2GB的视频文件&#xff1b;Gmail邮箱的附件大小限制为25MB&#xff1b;163邮箱的附件大小限制为2GB&#xff0c;但是为了保证文件传输的成功率&#xff0c;建议最好不…

SpringBoot 多环境开发配置文件

在开发过程中&#xff0c;往往开发环境和生产环境需要不同的配置。为了兼容两种运行环境&#xff0c;提高开发效率&#xff0c;可以使用多环境开发配置文件。 配置文件结构大概是这样&#xff1a; application.yml -主启动配置文件&#xff08;用于控制使用哪种环境配…

金和OA jc6 clobfield接口存在SQL注入漏洞

文章目录 产品简介漏洞概述指纹识别漏洞利用修复建议 产品简介 金和OA协同办公管理系统j6软件是一种综合性的协同办公解决方案&#xff0c;旨在提高企业内部的协作效率和工作效率。它提供了一系列功能和工具&#xff0c;帮助组织进行任务管理、日程安排、文件共享、团队协作和…

关于腾讯位置服务路线规划-微信小程序-插件未授权使用

我们点击添加插件会发现添加错误,主要是因为他说不开放给个人用户. 这是我们可以进入微信服务市场直接搜索腾讯位置服务路线规划 | 微信服务市场 (qq.com)点击添加插件即可完成

实战|Smartbi---意外的福利

目录 漏洞描述 影响版本 fofa语法 漏洞复现 构造poc 修复建议 本文由掌控安全学院 - beize 投稿 漏洞描述 Smartbi在安装时会内置几个用户&#xff0c;在使用特定接口时&#xff0c;可绕过用户身份认证机制获取其身份凭证&#xff0c;随后可使用获取的身份凭证调用后台…

统计分析绘图软件 GraphPad Prism 10 mac功能介绍

GraphPad Prism mac是一款专业的统计和绘图软件&#xff0c;主要用于生物医学研究、实验设计和数据分析。 GraphPad Prism mac功能和特点 数据导入和整理&#xff1a;GraphPad Prism 可以导入各种数据格式&#xff0c;并提供直观的界面用于整理、编辑和管理数据。用户可以轻松地…