代码随想录算法训练营第29天 | 491.递增子序列 46.全排列 47.全排列II

非递减子序列

Alt
这道题与子集II比较相似,子序列也是子集,而且这里数组中也包含重复元素。但是这道题要有序的序列,所以不能对原来的数组先进行排序。但我们前一篇文章总结过:子集问题中涉及重复元素时,是需要排序的。
这里还需要细化总结一下,树层去重可以做到去除元素和排列顺序都相同的组合,无法去除 [4,1,4] 和 [4,4,1] 这样顺序不同的情况。而经过排序之后这个问题就解决了,这道题本身就限制了元素顺序,所以只要能去除元素和顺序都相同的重复组合就行了。

class Solution{
public:
	vector<vector<int>> result;
	vector<int> path;
	void backtracking(vector<int>& nums, int startIndex){
		if(path.size() > 1){  // 要求path中有两个及以上元素
			result.push_back(path);  // 不要return,因为要在递归过程中始终记录
		}
		unordered_set<int> uset;  // 进行树层去重
		for(int i = startIndex; i < nums.size(); i++){
			if((!path.empty() && nums[i] < path.back()) 
			|| uset.find(nums[i]) != uset.end()){
				// 如果不空,当前元素破坏了单调不减,或者树层发生了重复,放弃当前元素
				continue;
			}
			uset.insert(nums[i]);
			path.push_back(nums[i]);
			backtracking(nums, i + 1);  // 元素只能使用一次
			path.pop_back();
		}
	}
	vector<vector<int>> findSubsequences(vector<int>& nums){
		result.clear();
		path.clear();
		backtracking(nums, 0);
		return result;
	}
};

这里也可以用数组实现哈希表,代码的性能更好,因为数组中元素值范围不大。

全排列

Alt
全排列问题需要注意 [1,2] 和 [2,1] 是两种不同的排列,因此不需要 startIndex 避免组合的重复出现。但是仍然需要在树枝上记录用过的元素,避免一个排列中元素重复。

class Solution{
public:
	vector<vector<int>> result;
	vector<int> path;
	void backtracking(vector<int>& nums, vector<bool>& used){
		if(path.size() == nums.size()){
			result.push_back(path);
			return;
		}
		for(int i = 0; i < nums.size(); i++){
			if(used[i] == true)  continue;
			used[i] = true;
			path.push_back(nums[i]);
			backtracking(nums, used);
			path.pop_back();
			used[i] = false;
		}
	}
	vector<vector<int>> permute(vector<int>& nums){
		result.clear();
		path.clear();
		vector<bool> used(nums.size(), false);
		backtracking(nums, used);
		return result;
	}
};

全排列II

Alt
这道题与组合问题II也是相似的,数组中有重复的元素。所以仍然可以借鉴全排列和组合问题II的思路。注意需要判断used[i - 1],因为不能用 startIndex 在树枝上做去重。有趣的是,判断used[i - 1] == trueused[i - 1] == false都是可以的,可以画图验证做树层去重剪枝效果更好。

class Solution{
public:
	vector<vector<int>> result;
	vector<int> path;
	void backtracking(vector<int>& nums, vector<bool>& used){
		if(path.size() == nums.size()){
			result.push_back(path);
			return;
		}
		for(int i = 0; i < nums.size(); i++){
			if(i > 0 && nums[i] == nums[i - 1] && !used[i - 1]){
				continue;
			}
			if(!used[i]){
				used[i] = true;
				path.push_back(nums[i]);
				backtracking(nums, used);
				path.pop_back();
				used[i] = false;
			}
		}
	}
	vector<vector<int>> permuteUnique(vector<int>& nums){
		result.clear();
		path.clear();
		sort(nums.begin(), nums.end());
		vector<bool> used(nums.size(), false);
		backtracking(nums, used);
		return result;
	}
};
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值