回溯题中借助哈希法来巧妙去重的操作

今天总结一下回溯法以来做过的这些题,我又发现一个困扰了我的问题,就是在491. 非递减子序列、46. 全排列、47. 全排列 II中都有涉及到用哈希法,去记录曾经用过的元素,下面来总结一下吧。
首先得知道,为什么会用到哈希法?

为什么会用到哈希法?

在491. 非递减子序列问题中,是因为集合中有相同的元素,而结果又不能出现相同的组合,如本题的示例1 [4,6,7,7]中,当4与倒数第二个7组合时,一个答案为[4,7],而下一个,与倒数第一个7则不能再组合了,即不能出现两个[4,7],借代码随想录里的图来理解,就是同一层不能重复使用相同的数字,如图:Alt
而这时,就可以利用一个哈希表,来保存每一层使用过哪些元素,注意这里的每一层这个要求,结合下面的代码理解一下:

非递减子序列代码:

public void backtracking(int[] nums, int startIndex) {if(temp.size() >= 2) {result.add(new ArrayList<>(temp));}Set<Integer> set = new HashSet<>(); // 在每一层递归中,设置一个Set哈希表,记录这一层使用过哪些元素for(int i = startIndex; i < nums.length; i++) {// 解决递增以及重复的问题if(!temp.isEmpty() && (nums[i] < temp.get(temp.size() - 1)) || set.contains(nums[i])) {continue;}temp.add(nums[i]);set.add(nums[i]);backtracking(nums, i + 1);temp.remove(temp.size() - 1);}}

而在全排列问题中,则不是记录每一层,而是记录某条树枝上(即整个递归中)哪些元素使用过,例如46. 全排列中的示例1的数据[1,2,3],由于排列的特点,for循环每次都从0开始,但是要保证不能重复选某个元素,所以要用哈希表来记录,哪些数字用了,哪些没有,下面是代码,重在理解“某条树枝上(即整个递归中)哪些元素使用过”。

全排列代码:

class Solution {List<Integer> temp = new ArrayList<>();List<List<Integer>> result = new ArrayList<>();boolean[] table = new boolean[21];  // 哈希表,记录整个递归中使用过的元素public List<List<Integer>> permute(int[] nums) {backtracking(nums);return result;}public void backtracking(int[] nums) {if(temp.size() == nums.length) {result.add(new ArrayList<>(temp));return;}for(int i = 0; i < nums.length; i++) {// 去重操作if(!temp.isEmpty() && table[nums[i] + 10]) {continue;}temp.add(nums[i]);table[nums[i] + 10] = true;    // 设置哈希表,使用过的元素记录backtracking(nums);temp.remove(temp.size() - 1);  // 回溯table[nums[i] + 10] = false;   // 回溯之后,使用过的记录也清空}}
}

而这道题的升级版:47. 全排列 II,由于出现了重复元素,为了还能全排列,设计哈希表时,就不是记录值而是记录使用过的元素的位置了,因为排列时,使用的数字位置不同,排列也是不同的,所以对于全排列 II,哈希表记录的是整个递归中,使用的是哪个位置的元素,下面是代码:

全排列 II代码:

class Solution {List<Integer> temp = new ArrayList<>();List<List<Integer>> result = new ArrayList<>();public List<List<Integer>> permuteUnique(int[] nums) {Arrays.sort(nums);// 排序是为了后面的组间去重boolean[] used = new boolean[nums.length]; 	// 其实这个放在全局变量也行backtracking(nums, used);return result;}public void backtracking(int[] nums, boolean[] used) {if(temp.size() == nums.length) {result.add(new ArrayList<>(temp));return;}for(int i = 0; i < nums.length; i++) {if(used[i]) {		// 判断当前这个位置的元素是否在前面的递归中使用过,如果使用过,则排列下面的元素 continue;}temp.add(nums[i]);used[i] = true;backtracking(nums, used);temp.remove(temp.size() - 1);used[i] = false;	// 回溯不要忘了把丢弃的元素也设置为“未使用”while(i + 1 < nums.length && nums[i +1] == nums[i]) {// 去重操作i++;}}}
}

总结

可以看出来,其实用上哈希法,就是两种情况,要么是要判断同一层是否使用过,要么是要判断整个递归中是否使用过,对应就是哈希表定义的地方不同,要判断同一层是否使用过,哈希表定义在递归函数里;要判断整个递归中是否使用过,哈希表定义在全局变量中或者定义在递归函数之外

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BulingBuling - 《研究巴菲特》 [ Buffettology ]

研究巴菲特 使沃伦-巴菲特成为世界上最著名的投资者的那些以前未曾解释过的技术 作者&#xff1a;玛丽-巴菲特 Buffettology The Previously Unexplained Techniques That Have Made Warren Buffett The Worlds Most Famous Investor By Mary Buffett 内容提要 《Buffetto…

机器学习中7种常用的线性降维技术总结

上篇文章中我们主要总结了非线性的降维技术&#xff0c;本文我们来总结一下常见的线性降维技术。 1、Principal Component Analysis (PCA) Principal Component Analysis (PCA) 是一种常用的降维技术&#xff0c;用于将高维数据集转换为低维表示&#xff0c;同时保留数据集的…

如何打开Windows 10及更低版本系统的控制面板?这里提供详细步骤

Windows中的控制面板是一组小程序,有点像小程序,可以用来配置操作系统的各个方面。 例如,“控制面板”中的一个小程序允许你配置鼠标指针的大小(以及其他内容),而另一个则允许你调整所有与声音相关的设置。 其他小程序可用于更改网络设置、设置存储空间、管理显示设置等…

什么是位段?位段的作用是什么?他与结构体有什么关系?

目录 1.什么是位段&#xff1f; 2.位段的内存分配 判断当前机器位段的内存分配形式 1.什么是位段&#xff1f; 位段的声明和结构是类似的&#xff0c;有两个不同&#xff1a; 1.位段的成员必须是 int、unsigned int 或signed int或char 。 2.位段的成员名后边有一个冒号和…

基于RTOS的嵌入式软件开发与可靠性提升

&#xff08;本文为简单介绍&#xff0c;观点来自网络&#xff09; 随着科技的快速发展&#xff0c;嵌入式系统无所不在&#xff0c;从你的智能手表到汽车的自动驾驶系统&#xff0c;它们都在静静地改变我们的世界。而在这一切的背后&#xff0c;实时操作系统&#xff08;RTOS&…

CTFshow web(文件上传158-161)

web158 知识点&#xff1a; auto_append_file 是 PHP 配置选项之一&#xff0c;在 PHP 脚本执行结束后自动追加执行指定的文件。 当 auto_append_file 配置被设置为一个文件路径时&#xff0c;PHP 将在执行完脚本文件的所有代码后&#xff0c;自动加载并执行指定的文件。 这…

【HarmonyOS】hdc 环境变量设置

hdc&#xff08;HarmonyOS Device Connector&#xff09;是 HarmonyOS 为开发人员提供的用于调试的命令行工具&#xff0c;通过该工具可以在 windows/linux/mac 系统上与真实设备或者模拟器进行交互。 hdc 工具通过 HarmonyOS SDK 获取&#xff0c;存放于 /Huawei/Sdk/openhar…

网络原理-TCP_IP(6)

网络层 在复杂的网络环境中确定一个合适的路径. IP协议 与TCP协议并列,都是网络体系中最核心的协议. 基本概念 主机:配有IP地址,但是不进行路由控制的设备; 路由器:即配有IP地址,又能进行路由控制; 节点:主机和路由器的统称; 协议头格式 4位版本号(version):指定IP协议的版…

Rust 基本环境安装

rust 基本介绍请看上一篇文章&#xff1a;rust 介绍 rustup 介绍 rustup 是 Rust 语言的安装器和版本管理工具。通过 rustup&#xff0c;可以轻松地安装 Rust 编译器&#xff08;rustc&#xff09;、标准库和文档。它也允许你切换不同的 Rust 版本或目标平台&#xff0c;以及…

JDBC 核心 API

引入 mysql-jdbc 驱动 驱动 jar 版本的选择&#xff1a;推荐使用 8.0.25&#xff0c;省略时区设置java 工程导入依赖 项目创建 lib 文件夹导入驱动依赖 jar 包jar 包右键 - 添加为库 JDBC 基本使用步骤 注册驱动获取连接创建发送 sql 语句对象发送 sql 语句&#xff0c;并获…

安卓学习笔记之五:Android Studio_骰子案例3(Kotlin搭配 Jetpack Compose实现)

使用 Compose 创建一款交互式 Dice Roller Android 应用。 完成&#xff1a; 定义可组合函数。使用组合创建布局。使用 Button 可组合项创建按钮。导入 drawable 资源。使用 Image 可组合项显示图片。使用可组合项构建交互式界面。使用 remember 可组合项将组合中的对象存储到…

在线Windows鼠标主题转换器(ani动态鼠标改为Xcur)

文章目录 前言在哪访问如何使用惨淡的界面简单粗暴的使用方法目前的bug 前言 在这篇文章中&#xff0c;我使用一些方法把转换脚本包装成了在线服务&#xff0c;现在我将说明如何使用服务。 在哪访问 还是说明一下&#xff0c;访问链是这个&#xff1a;https://www.sakebow.c…