AcWing 51 数字排列

题目描述:

输入一组数字(可能包含重复数字),输出其所有的排列方式。

样例

输入:[1,2,3]

输出:
      [
        [1,2,3],
        [1,3,2],
        [2,1,3],
        [2,3,1],
        [3,1,2],
        [3,2,1]
      ]

分析:

本题与普通的全排列问题的不同之处在于可能存在重复数字,不能输出重复的序列,比如1123和1123,。解决问题的关键在于定序,紫书上子集生成那节增量构造法便讲到了这种定序的技巧。

1123,我们规定第二个1只能出现在第一个1之后便不会出现重复的枚举了。

我们需要先给nums排序,方便定序。dfs与常规的排列问题一样,需要有当前的位置u,状态压缩时表示的状态state,这里为了定序增加了一个start,如果没有重复数字,start便等于0,可以从第一个位置开始放置数字,否则就要从上一个重复的数字后面放置数字。

在分析具体的算法之前,先总结下相似的dfs题目。

1. AcWing 92 递归实现指数型枚举

用向量保存中间状态时,只需要问题的规模n和当前数字cur作为dfs参数,递归体内写选择该数和不选择该数的情况,当达到一定的递归深度,输出方案即可。

而不使用向量,直接状态压缩,用st存储状态信息,这里的st就相当于向量的作用,只是更加节省空间了。

2.AcWing 93 递归实现组合型枚举

这题与之前题的区别就是在所有子集中输出固定个数的序列,在上一题的算法中加上已选择数的个数k,并加以判断即可,其他参数的含义并未改变。

3.AcWing 94 递归实现排列型枚举

这题是经典的全排列问题,与上面两题不同的是,上面的题目涉及的是选与不选某数,而这题所有数都必须被选,只是被选择的先后有所不同。所以使用状态压缩时依旧可以用st表示中间状态,但是到达边界时,st二进制的各个位置必然都是1,所以还需要向量来存储枚举的先后,并输出最终结果。

不妨回忆下此题代码

void dfs(int d,int st){
	if(d == n ){
		for(int i = 0;i < t.size();i++){
			cout<<t[i]+1<<" ";
		}
		cout<<endl;
		return;
	}
	for(int i = 0;i < n;i++){
		if(!(st >> i & 1)){
			t.push_back(i);
			dfs(d+1,st | 1 << i );
			t.pop_back();
		}
	}
}

 如同AcWing 47 二叉树中和为某一值的路径中所分析的那样,递归的执行过程是,先不断把未放进向量的数放进去,放满了就再退出部分数,继续放,而在dfs的末尾加上这一句pop_back()操作就很完美的完成了这一复杂的回溯过程。

下面正式分析本题。

与刚才讨论全排列问题不同的是,交互式问题可能多几个向量保存结果,并且要加上定序。

我们从向向量放第一个数开始,直到放满指定个数的数结束。比如放置12344,1,2,3,4依次放入,再放入4时只能从下一个位置开始放。而对于不重复的123,我们枚举的范围是从0到结尾的。这里不需要写回溯语句的原因是,向量的值不是像之前那样不断压入弹出,而是从题目给的向量中直接赋值。这里最后一行的递归语句state + (1<<i)和上题的st | 1 << i功能是一样的。

class Solution {
public:
    vector<vector<int> > ans;
    vector<int> path;
    vector<vector<int>> permutation(vector<int>& nums) {
        path.resize(nums.size());
        sort(nums.begin(),nums.end());
        dfs(nums,0,0,0);
        return ans;
    }
    void dfs(vector<int> &nums,int u,int start,int state){
        if(u == nums.size()){
            ans.push_back(path);
            return;
        }
        if(!u || nums[u] != nums[u-1])  start = 0;
        for(int i = start;i < nums.size();i++){
            if(!(state >> i & 1)){
                path[i] = nums[u];
                dfs(nums,u+1,i+1,state + (1 << i));
            }
        }
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值