leetcode-047-全排列2

该篇文章介绍了一个Java程序,用于解决给定可重复数字序列的全排列问题,同时去除重复的排列组合。程序通过先对序列排序,然后利用深度优先搜索(DFS)和回溯策略进行递归遍历,避免重复的分支。在DFS过程中,通过判断当前元素与前一个元素的关系来确定是否为重复分支,并进行剪枝优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目及测试

package pid047;
/*47. 全排列 II
给定一个可包含重复数字的序列 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
*/

import java.util.List;

public class main {
	
	public static void main(String[] args) {
		int [][] testTable = {{1,1,2},{1,2,3},{0,1,0,0,9},{1}};
		for (int[] ito : testTable) {
			test(ito);
		}
	}
		 
	private static void test(int[] ito) {
		Solution solution = new Solution();
		List<List<Integer>> rtn;
		long begin = System.currentTimeMillis();
		for(int i=0;i<ito.length;i++){
			System.out.print(ito[i]+"  ");
		}
		System.out.println();
		//开始时打印数组
		
		rtn= solution.permuteUnique(ito);//执行程序
		long end = System.currentTimeMillis();	
		
		System.out.println("rtn=" );
		for(int i=0;i<rtn.size();i++){
			for(int j=0;j<rtn.get(i).size();j++){
				System.out.print(rtn.get(i).get(j)+" ");
			}
			System.out.println();
		}
		System.out.println();
		System.out.println("耗时:" + (end - begin) + "ms");
		System.out.println("-------------------");
	}

}

自己的解法有案例过不去,去重有问题

解法1(别人的)

为什么要先排序 因为结果是要去重的,而结果都是列表形式的,那列表怎么判重呢?就是排序之后一个一个的比较,所以那还不如先排序之后再计算,再计算的过程中判断是否有重复,免得对每个结果再做一次排序去重操作。

怎么判断当前分支是否是重复的分支。 因为之前已经拍好序了,所以当前元素nums[i] 如果和前一个元素相同 ,即nums[i] == nums[i-1]就说明该分支 有可能 是重复的。 但是这个相等条件有两种可能 一种是,1 1‘ 2,也就是选择完1之后再选择第二个1,两个元素虽然重复,但是第二个元素是前一个元素的下一层,这时是没有问题的。 另一种是之前的 同层 分支已经有 1 1‘ 2了,这次的选择是 1‘ 1 2 。两个元素重复,且重的是同层路径。那就说明是重复分支。

具体区分的办法是 nums[i-1] 的used状态是被选择的,那么说明当前的nums[i] 是 nums[i-1]的下一层路径。 否则如果 nums[i-1] 的状态是没被选择的,那么说明当前 的nums[i] 是nums[i-1] 同层路径。

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;

public class Solution {

    public List<List<Integer>> permuteUnique(int[] nums) {
        int len = nums.length;
        List<List<Integer>> res = new ArrayList<>();
        if (len == 0) {
            return res;
        }

        // 排序(升序或者降序都可以),排序是剪枝的前提
        Arrays.sort(nums);

        boolean[] used = new boolean[len];
        // 使用 Deque 是 Java 官方 Stack 类的建议
        Deque<Integer> path = new ArrayDeque<>(len);
        dfs(nums, len, 0, used, path, res);
        return res;
    }

    private void dfs(int[] nums, int len, int depth, boolean[] used, Deque<Integer> path, List<List<Integer>> res) {
        if (depth == len) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = 0; i < len; ++i) {
            if (used[i]) {
                continue;
            }

            // 剪枝条件:i > 0 是为了保证 nums[i - 1] 有意义
            // 写 !used[i - 1] 是因为 nums[i - 1] 在深度优先遍历的过程中刚刚被撤销选择
            if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
                continue;
            }

            path.addLast(nums[i]);
            used[i] = true;

            dfs(nums, len, depth + 1, used, path, res);
            // 回溯部分的代码,和 dfs 之前的代码是对称的
            used[i] = false;
            path.removeLast();
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值