一文搞懂全排列、组合、子集问题 (3)

使用HashSet这种方式这里就不讨论啦,我们在进行交换swap的时候从前往后,前面的确定之后就不会在动,所以我们要慎重考虑和谁交换。比如1 1 2 3第一个数有三种情况而不是四种情况(两个1 1 2 3为一个结果)

1 1 2 3 // 0 0位置交换
2 1 1 3 // 0 2位置交换
3 1 2 1 // 0 3位置交换

另外比如3 1 1序列,3和自己交换,和后面两个1只能和其中一个进行交换,我们这里可以约定和第一个出现的进行交换,我们看一个图解部分过程:

邻里互换一个过程

所以,当我们从一个index开始的时候要记住以下的规则:同一个数只交换一次(包括值等于自己的数)。在判断后面值是否出现的时候,你可以遍历,也可以使用hashSet().当然这种方法的痛点就是判断后面出现的数字效率较低。所以在可能重复的情况这种方法效率一般般。

具体实现的代码为:

public List<List<Integer>> permuteUnique(int[] nums) { List<List<Integer>> list=new ArrayList<List<Integer>>(); arrange(nums, 0, nums.length-1, list); return list; } private void arrange(int[] nums, int start, int end, List<List<Integer>> list) { if(start==end) { List<Integer>list2=new ArrayList<Integer>(); for(int a:nums) { list2.add(a); } list.add(list2); } Set<Integer>set=new HashSet<Integer>(); for(int i=start;i<=end;i++) { if(set.contains(nums[i])) continue; set.add(nums[i]); swap(nums,i,start); arrange(nums, start+1, end, list); swap(nums, i, start); } } private void swap(int[] nums, int i, int j) { int team=nums[i]; nums[i]=nums[j]; nums[j]=team; } 组合问题

组合问题可以认为是全排列的变种,问题描述(力扣77题):

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

示例:

输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]

分析:

这个问题经典回溯问题。组合需要记住只看元素而不看元素的顺序,比如a b和b a 是同一个组合。要避免这样的重复是核心,而避免这样的重复,需要借助一个int类型保存当前选择元素的位置,下次只能遍历选取下标位置后面的数字,而k个数,可以通过一个数字类型来记录回溯到当前层处理数字的个数来控制。

全排列和组合的一些区别

具体实现也很容易,需要创建一个数组储存对应数字,用boolean数组判断对应位置数字是否使用,这里就不用List存储数字了,最后通过判断boolean数组将数值添加到结果中也是可行的。实现代码为:

class Solution { public List<List<Integer>> combine(int n, int k) { List<List<Integer>> valueList=new ArrayList<List<Integer>>();//结果 int num[]=new int[n];//数组存储1-n boolean jud[]=new boolean[n];//用于判断是否使用 for(int i=0;i<n;i++) { num[i]=i+1; } List<Integer>team=new ArrayList<Integer>(); dfs(num,-1,k,valueList,jud,n); return valueList; } private void dfs(int[] num,int index, int count,List<List<Integer>> valueList,boolean jud[],int n) { if(count==0)//k个元素满 { List<Integer>list=new ArrayList<Integer>(); for(int i=0;i<n;i++) { if (jud[i]) { list.add(i+1); } } valueList.add(list); } else { for(int i=index+1;i<n;i++)//只能在index后遍历 回溯向下 { jud[i]=true; dfs(num, i, count-1, valueList,jud,n); jud[i]=false;//还原 } } } } 子集

子集问题和组合有些相似。这里讲解数组中无重复和有重复的两种情况。

无重复数组子集

问题描述(力扣78题):

给你一个整数数组 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 中的所有元素 互不相同

子集和上面的组合有些相似,当然我们不需要判断有多少个,只需要按照组合回溯的策略递归进行到最后每进行的一次递归函数都是一种情况都要加入到结果中(因为采取的策略不会有重复的情况)。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwzwy.html