【leetCode】全排列问题,不可重复+可重复
程序员文章站
2022-07-02 11:32:38
思路:回溯算法的方式进行寻找数字,只需要一个arraylist 就可以,不过记住在进行复制的时候及进行深拷贝,不然都是一个引用,呜呜!对于可重复的情况下,要在进行回溯之前进行剪枝,想到这个问题的本质的问题。46. 全排列给定一个 没有重复 数字的序列,返回其所有可能的全排列。示例:输入: [1,2,3]输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]47. 全排列 II给定一个可包含重复数字的序列,返回所有不重复的全排列...
思路:回溯算法的方式进行寻找数字,只需要一个arraylist 就可以,不过记住在进行复制的时候及进行深拷贝,不然都是一个引用,呜呜!
对于可重复的情况下,要在进行回溯之前进行剪枝,想到这个问题的本质的问题。
46. 全排列
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
47. 全排列 II
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
第一次写的比较复杂
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> ans=new ArrayList<>();
List<Integer> single=new ArrayList<>();
ans= findpermute(ans,nums,single);
return ans;
}
private List<List<Integer>> findpermute(List<List<Integer>> ans, int[] nums,List<Integer> single) {
for (int i = 0; i < nums.length; i++) {
if (! single.contains(i)){
single.add(i);
if (single.size()==nums.length){
List<Integer> temp= new ArrayList<>();
for (int i1 = 0; i1 < single.size(); i1++) {
temp.add(nums[single.get(i1)]);
}
ans.add(temp);
}else {
ans= findpermute(ans, nums,single);
}
single.remove(single.indexOf(i));
}
}
return ans;
}
}
修改之后是这个样子
可以直接使用增强for ,而且直接在方法一开始做判断;最重要的是拷贝的时候直接使用
new ArrayList<>(single)
public static List<List<Integer>> permute(int[] nums) {
List<List<Integer>> ans=new ArrayList<>();
findpermute(ans,nums,new ArrayList<>());
return ans;
}
private static void findpermute(List<List<Integer>> ans, int[] nums,List<Integer> single) {
if (single.size()==nums.length) ans.add(new ArrayList<>(single));
for (int num : nums) {
if (!single.contains(num)) {
single.add(num); // 动态维护数组
findpermute(ans, nums, single); // 继续递归填下一个数
single.remove((Integer) num); // 撤销操作,回溯
}
}
}
可重复全排列
第一次自己的想法写的,超时,思考一下有没有什么剪枝的方法
public static List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> ans=new ArrayList<>();
findpermuteUnique(ans,nums,new ArrayList<>());
return ans;
}
private static void findpermuteUnique(List<List<Integer>> ans, int[] nums,List<Integer> single) {
if (single.size()==nums.length)
{
List<Integer> temp=new ArrayList<>();
for(int i=0;i<single.size();i++) temp.add(nums[single.get(i)]);
int flag=0;
for (List<Integer> an : ans){
flag=0;
for (int i = 0; i < an.size(); i++) if (an.get(i).equals(temp.get(i))) flag++;
if (flag==temp.size()) break;
}
if (flag!=temp.size()) ans.add(temp);
}
for (int i = 0; i < nums.length; i++) {
if (!single.contains(i)) {
single.add(i); // 动态维护数组
findpermuteUnique(ans, nums, single); // 继续递归填下一个数
single.remove((Integer) i); // 撤销操作,回溯
}
}
}
要解决重复问题,我们只要设定一个规则,保证在填第 idx 个数的时候重复数字只会被填入一次即可。而在本题解中,我们选择对原数组排序,保证相同的数字都相邻,然后每次填入的数一定是这个数所在重复数集合中「从左往右第一个未被填过的数字」,即如下的判断条件:
if(vis[i] || i>0 && nums[i-1]==nums[i] && vis[i-1]) continue;
这个判断条件保证了对于重复数的集合,一定是从左往右逐个填入的。
假设我们有 33 个重复数排完序后相邻,那么我们一定保证每次都是拿从左往右第一个未被填过的数字,即整个数组的状态其实是保证了 [未填入,未填入,未填入] 到 [填入,未填入,未填入],再到 [填入,填入,未填入],最后到 [填入,填入,填入] 的过程的,因此可以达到去重的目标。
private static boolean[] vis ;
public static List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> ans=new ArrayList<>();
vis=new boolean[nums.length];
List<Integer> perm=new ArrayList<>();
Arrays.sort(nums);
findpermuteUnique(ans,0,nums,perm);
return ans;
}
private static void findpermuteUnique(List<List<Integer>> ans,int idx, int[] nums,List<Integer> perm) {
if(idx == nums.length){
ans.add(new ArrayList<>(perm));
}
for (int i = 0; i < nums.length; i++) {
if(vis[i] || i>0 && nums[i-1]==nums[i] && vis[i-1]) continue;
perm.add(nums[i]);
vis[i]=true;
findpermuteUnique(ans,idx+1,nums,perm);
perm.remove(idx); //移除的是序号
vis[i]=false;
}
}
本文地址:https://blog.csdn.net/weixin_42462804/article/details/109240284