全排列Ⅱ(中等难度,加入剪枝操作)

简介: 全排列Ⅱ(中等难度,加入剪枝操作)

目录

题目概述(中等难度)

思路与代码

思路展现

代码示例

题目概述(中等难度)

2.png


题目链接:

点我进入leetcode


思路与代码

思路展现

这道题目就是经典的回溯+DFS(深度优先搜索遍历),在这里我就不再给出我关于回溯的讲解了,leetcode的大佬已经帮我们总结到位了,只需要跟着他的题解走即可,在这里宣传以下weiwei大佬是一位非常厉害的大佬,除了他自己写的题解意外,也开设了自己的github网站帮助大家学习算法,在这里我都会将其网站贴到下面:

全排列Ⅱweiwei大佬题解链接

weiwei大佬github链接


这道题目是全排列题目的再进一步延申,只不过这道题目加入了两个条件,第一个就是这个序列中的元素有重复的,第二个就是需要按照任意顺序返回不重复的,在之前的全排列题目中我们的序列没有重复元素,所以最终我们返回的全排列就是没有重复元素的,而这次我们序列是有重复元素的,那么最终我们返回的全排列就肯定有重复元素,所以此时就需要用到剪枝,什么是剪枝以及如何用大家直接看weiwei大佬的题解就行啦,链接我已经放到了上面.


其实这段代码针对于之前的全排列其实就只多了几行代码,如下所示:

第一处:

首先要对我们的数组进行排序,因为数组有序是剪枝的前提

Arrays.sort(nums);

第二处:


if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
    continue;
}

我们来解释下为什么要加上面的代码

1:首先一个比较容易想到的办法是在结果集中去重。但是问题来了,这些结果集的元素是一个又一个列表,对列表去重不像用哈希表对基本元素去重那样容易。


如果要比较两个列表是否一样,一个容易想到的办法是对列表分别排序,然后逐个比对。既然要排序,我们就可以 在搜索之前就对候选数组排序,一旦发现某个分支搜索下去可能搜索到重复的元素就停止搜索,这样结果集中不会包含重复列表,所以此时我们用到了剪枝.

2:

为什么要加上nums[i] == nums[i - 1]?

例如对于【1,1’,2】这个数组,如果我们加上这个条件的话,相当于1’这个会产生与之前1相同的全排列结果的数字就不会再进行判断,直接跳到2去

但此时同学们思考一个问题,光加上这个条件够吗?

答:当然不够,原因如下

假设只判断这两个是否相等,很容易将我们本来符合条件的全排列删掉,例如假设如果是【1,1,2】这种组合,最终输出的结果是不存在全排列,因为前后只要重复就全部被剪掉了,所以说我们最终需要加上额外的判断条件

2.png

放到我们OJ的测试用例里面我们会发现有如下测试用例不通过:

2.png


此时为什么会有上面的问题出现,这是因为我们在判断具有重复元素的时候,并没有判断这两个重复的这个元素到底是在同一路径还是同一层,如果是同一路径是不能被剪枝的,而在同一层是可以被剪枝的,而这时我们判断两个重复的元素是在同一条路径还是在同层路径的标准是nums[i - 1] == false来进行判断的,假如此时nums[i - 1] = false的话,就说明我们这两个重复元素是在同层:如下所示:此类情况是必须进行剪枝的:原因是我们之前的1已经被选择过,并且状态从true置为了false,而此时这个新的1跟刚才1情况相同,所以就不需要再走一遍了,直接进行剪枝就好

2.png

而当nums[i - 1] = true的话,此类情况如下:

2.png

这种情况就说明是不能剪枝的.

总结:

2.png


代码示例

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
      //len代表数组的长度
       int len = nums.length;
       //path是一个双端队列,使用Deque的原因是官方题解所给出的
       Deque<Integer> path = new LinkedList<>();
       //设置我们的res
       List<List<Integer>> res = new ArrayList<>();
       //设置我们所给定的nums数组中每个元素的初始状态为false
       boolean[] used = new boolean[len];
       if(len == 0) {
           return res;
       }
       //排序是剪枝的前提
       Arrays.sort(nums);
       dfs(path,used,nums,res,0,len);
       return res;
    }
    /*
    dfs方法参数介绍:
    path表示双端队列,用于存储我们所选择的路径上的数字
    boolean数组用于表示数字的选择与被选择,被选择为true,没有为false
    res用于存储最后的返回结果
    我们递归结束的条件与我们nums数组的长度len以及二叉树深度depth有很大的关系
    */
    public void dfs(Deque<Integer> path , boolean[] used , int[] nums , List<List<Integer>> res , int depth , int len) {
       if(depth == len) {
           //注意这里的写法其实是一个简便写法,就是直接实例化一个list集合然后将双端队列参数直接传入进去
           res.add(new ArrayList<>(path));
           //声明我们的递归终止条件
           return;
       }
       //!used[i]就表示此时假设这个数字还没有被选中,它的反就一定为true,然后才能进入到下面的语句
           for(int i = 0 ; i < len ; i++) { 
               if(!used[i]) {
               // 剪枝条件:i > 0 是为了保证 nums[i - 1] 有意义
               // 写 !used[i - 1] 是因为 nums[i - 1] 在深度优先遍历的过程中刚刚被撤销选择
               if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
                   continue;
               }
               //选中后将数字加入到path中
               path.addLast(nums[i]);
               //选中后将状态置为true
               used[i] = true;
               //上面我们选择元素完毕后就开始我们的递归
               dfs(path  ,used , nums , res , depth + 1 , len);
               //进行完递归操作后接下来就需要我们进行撤回操作
               //1:撤回操作的第一步是将状态置为false
               used[i] = false;
               //2:撤回操作的第二步是将数字从path中移除
               path.removeLast();
           }
       }
    }
}

2.png

相关文章
|
定位技术
阿里架构总监一次讲透中台架构,13页PPT精华详解,建议收藏!
本文整理了阿里几位技术专家,如架构总监 谢纯良,中间件技术专家 玄难等几位大牛,关于中台架构的几次分享内容,将业务中台形态、中台全局架构、业务中台化、中台架构图、中台建设方法论、中台组织架构、企业中台建设实施步骤等总共13页PPT精华的浓缩,供大家学习借鉴。
37026 113
|
7月前
|
机器学习/深度学习 算法 Serverless
《当朴素贝叶斯遇上核函数:一场创新的技术融合》
朴素贝叶斯算法基于贝叶斯定理和特征条件独立假设,广泛应用于文本分类、垃圾邮件过滤等场景。核函数通过将数据映射到高维空间解决线性不可分问题,在支持向量机中表现出色。结合两者,利用核函数挖掘非线性关系,可提升朴素贝叶斯对复杂数据的处理能力。然而,这带来了计算复杂性和参数选择的挑战,需采用近似计算和交叉验证等方法应对。这种结合为改进朴素贝叶斯提供了新方向,未来有望在更多领域广泛应用。
120 26
|
6月前
|
机器学习/深度学习 人工智能 vr&ar
LHM:单图生成3D动画人!阿里开源建模核弹,高斯点云重构服装纹理
阿里巴巴通义实验室开源的LHM模型,能够从单张图像快速重建高质量可动画化的3D人体模型,支持实时渲染和姿态控制,适用于AR/VR、游戏开发等多种场景。
1407 0
LHM:单图生成3D动画人!阿里开源建模核弹,高斯点云重构服装纹理
|
数据采集 Web App开发 存储
Java爬虫第四篇:使用selenium、Jsoup 抓取图片
Java爬虫第四篇:使用selenium、Jsoup 抓取图片
866 0
|
11月前
|
SQL NoSQL Java
springboot操作nosql的mongodb,或者是如何在mongodb官网创建服务器并进行操作
本文介绍了如何在Spring Boot中操作NoSQL数据库MongoDB,包括在MongoDB官网创建服务器、配置Spring Boot项目、创建实体类、仓库类、服务类和控制器类,以及如何进行测试。
158 1
springboot操作nosql的mongodb,或者是如何在mongodb官网创建服务器并进行操作
|
11月前
|
负载均衡 监控 算法
Nginx入门 -- 深入了解Nginx负载均衡
Nginx入门 -- 深入了解Nginx负载均衡
147 0
|
监控 关系型数据库 MySQL
MySQL高可用MHA
MySQL高可用管理工具(MHA,Master High Availability)是一个用于自动管理MySQL主从复制的工具,它可以提供高可用性和自动故障转移。MHA由原版的MHA工具和MHA Manager组成,它们协同工作以实现自动主从切换和监控。
696 0
|
Shell Linux 开发工具
Git版本控制工具详解(一)
Git版本控制工具详解
290 0
Git版本控制工具详解(一)
|
架构师 微服务
什么是软件架构?架构的本质是什么?
定义 ”架构是什么“ 是件非常困难的事情,不同的组织对于软件架构有不同的定义,每个人心中也有自身对于系统架构定义的认知。就好比我们无法百分之百表述模型而只能产出模型不同维度的视图,对架构进行完备的定义是不可能的。
283 4
|
存储 缓存 网络协议
计算机网络:思科实验【2-MAC地址、IP地址、ARP协议及总线型以太网的特性】
计算机网络:思科实验【2-MAC地址、IP地址、ARP协议及总线型以太网的特性】