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

news2024/11/18 6:14:39

在这里插入图片描述

文章目录

  • 前言
  • 什么是决策树
  • 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;
            }
        }
    }
}

在这里插入图片描述

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

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

相关文章

(8)Linux Makefile | 依赖关系,依赖方法

&#x1f4ad;前言&#xff1a; 本篇文章会着重讲解Linux中的自动化构建代码工具: make/makefile的介绍与使用。 在Linux下编译代码时,每次都会输入 gcc code.c -o code.exe在删除可执行程序时,每次都会输入 rm -rf code.exe这样非常的不方便,很麻烦,于是乎学习自动化构建代…

Netty Review - ByteBuf 读写索引 详解

文章目录 概念Pre概述ByteBuf简介ByteBuf的主要特性结构APIByteBuf的创建读写操作示例引用计数操作其他常用操作 Code 演示 概念 Pre Netty Review - 探索ByteBuf的内部机制 概述 Netty的ByteBuf是一个强大的字节容器&#xff0c;用于处理字节数据。它提供了比Java标准库中的…

for命令语句

命令说明&#xff1a; 在批处理中&#xff0c;for是最为强大的命令语句&#xff0c;它的出现&#xff0c;使得解析文本内容、遍历文件路径、数值递增和递减等操作更加简单&#xff0c;配合if、call、goto等流程控制语句&#xff0c;更是可以实现脚本复杂的自动化、智能化操作。…

js基础入门

先来一点js基础&#xff0c;其实js大部分的时候都在处理对象或者数组。 对象四个基本操作&#xff1a;增删改查 掌握元素的增删改查&#xff0c;了解如何拷贝&#xff0c;深拷贝和浅拷贝的区别。详情见代码 <script>//创建对象一共有三种赋值声明的语法let obj{} //赋值…

Relocations for this machine are not implemented,IDA版本过低导致生成汇编代码失败

目录 1、问题描述 2、安卓app发生崩溃&#xff0c;需要查看汇编代码上下文去辅助分析 3、使用IDA打开.so动态库文件&#xff0c;提示Relocations for this machine are not implemented 4、IDA版本较老&#xff0c;不支持ARM64的指令集&#xff0c;使用7.0版本就可以了 5、…

【数据结构】二叉树的模拟实现

前言:前面我们学习了堆的模拟实现&#xff0c;今天我们来进一步学习二叉树&#xff0c;当然了内容肯定是越来越难的&#xff0c;各位我们一起努力&#xff01; &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:数据结构 &#x1f448; &…

创建个人网站(二)前端主页设计和编写一(太阳移动)

前言 以下内容纯纯当乐子来看就行&#xff0c;知识分享一下这样设计的原因&#xff0c;想看正文直接见下一节 为什么创建个人网站一之后几天没有动静了呢&#xff0c;一个是家里有事实在比较忙&#xff0c;第二个原因是没想到主页要设计成什么样&#xff0c;知道前两天问我姐什…

0基础学java-day22(多用户即时通信系统)

一、QQ 聊天项目演示 聊天通讯系统 在运运行过程出现的异常&#xff0c;应该是类的序列化不一致导致的 1 项目 QQ 演示 2 为什么选择这个项目 只做核心部分&#xff0c;界面相对弱化 3 项目开发流程 3.1 需求分析 3.2 界面设计 3.2.1 用户登录 3.2.2 拉取在线用户列表 …

对偶问题笔记(1)

目录 1 从 Lagrange 函数引入对偶问题2. 强对偶性与 KKT 条件3. 对偶性的鞍点特征 1 从 Lagrange 函数引入对偶问题 考虑如下优化问题 { min ⁡ f 0 ( x ) s . t f i ( x ) ≤ 0 , i 1 , ⋯ , p , h j ( x ) 0 , j 1 , ⋯ , q , x ∈ Ω , \begin{align} \begin{cases}\min…

Mimikatz 的使用(黄金票据的制作)

#江南的江 #每日鸡汤&#xff1a;孤独没有什么反义词&#xff0c;但他的近义词是自由&#xff0c;人生成功的道路上充满了孤独&#xff0c;那么也同样告诉你&#xff0c;你离成功后的自由不远了。 #初心和目标&#xff1a;在网络安全里高出名堂。。。 Mimikatz 本文分为两种…

kafka启动报错“输入行太长。 命令语法不正确“

下午想试一下本地安装启动一下 kafak&#xff0c;参考网上的安装文档&#xff0c;结果一直报错&#xff0c;最开始报的是这个错误&#xff1a; Classpath is empty. Please build the project first e.g. by running gradlew jarAll参考了很多网上的解决办法&#xff0c;最后…

科创金融的向善力量:浙商银行多措并举赋能科创企业,打造科技金融服务生态圈

近日&#xff0c;浙商银行科技金融服务发布会在杭州举行。 发布会以“智汇科创&#xff0c;善行未来”为主题&#xff0c;围绕科技金融服务“向善”新生态&#xff0c;浙商银行重磅推出科创企业全图景服务方案&#xff0c;正式发布科创积分贷&#xff0c;与浙江大学联合发布人…

【开源软件】最好的开源软件-2023-第五名 JHipster

自我介绍 做一个简单介绍&#xff0c;酒架年近48 &#xff0c;有20多年IT工作经历&#xff0c;目前在一家500强做企业架构&#xff0e;因为工作需要&#xff0c;另外也因为兴趣涉猎比较广&#xff0c;为了自己学习建立了三个博客&#xff0c;分别是【全球IT瞭望】&#xff0c;【…

翻译: LLMs大语言模型影响到高工资的的白领知识工作者 加速各行各业的自动化潜力 Automation potential across sectors

我们已经探讨了生成人工智能可能对您的工作有用&#xff0c;也讨论了分析其对企业的影响。现在&#xff0c;让我们拉远镜头&#xff0c;看看它对不同公司的工作角色以及对不同行业部门的影响。这个视频的结果对特定企业可能不那么直接可行&#xff0c;但也许这会帮助您思考并尝…

IDEA tomcat内存不足

-Xms256m -Xmx256m -XX:MaxNewSize256m -XX:MaxPermSize256m

LLMs推理框架总结

总结一下这些框架的特点&#xff0c;如下表所示&#xff1a; LLM推理有很多框架&#xff0c;各有其特点&#xff0c;下面分别介绍一下表中七个框架的关键点&#xff1a; vLLM&#xff1a;适用于大批量Prompt输入&#xff0c;并对推理速度要求高的场景&#xff1b;Text generat…

14、Kafka 请求是怎么被处理的

Kafka 请求是怎么被处理的 1、处理请求的 2 种常见方案1.1、顺序处理请求1.2、每个请求使用单独线程处理 2、Kafka 是如何处理请求的&#xff1f;3、控制类请求和数据类请求分离 无论是 Kafka 客户端还是 Broker 端&#xff0c;它们之间的交互都是通过 “请求 / 响应” 的方式完…

二叉搜索树第大K节点,剑指offer,力扣

目录 题目地址&#xff1a; 题目&#xff1a; 我们直接看题解吧&#xff1a; 解题方法&#xff1a; 难度分析&#xff1a; 审题目事例提示&#xff1a; 解题分析&#xff1a; 解题思路&#xff1a; 代码实现&#xff1a; 代码补充&#xff1a; 代码实现&#xff08;非递归&…

20倍压缩比!微软提出大模型提示压缩框架LLMLingua

近期&#xff0c;越来越多研究在探索大型语言模型&#xff08;LLM&#xff09;在实际应用中的推理和生成能力。随着 ChatGPT 等模型的广泛研究与应用&#xff0c;如何在保留关键信息的同时&#xff0c;压缩较长的提示成为当前大模型研究的问题之一。 为了加速模型推理并降低成本…

ViewBinding与DataBinding(视图绑定与数据双向绑定)

前言&#xff1a;心中纵是有所盼 严寒没有减 风很冷 我的手已渐蓝 前言 控件查找对于Android开发来说也是一部血泪史&#xff0c;一直为更有效的方案进行了多种方案的研究和探讨。findViewById() 过于繁琐&#xff0c;强制转换不安全&#xff1b;butterkniife 会存在众多臃肿的…