[LeetCode]Permutation全排列和去重全排列

一、问题描述:

借助这道题总结一下全排列问题吧

https://leetcode.com/problems/permutations/

Given a collection of distinct numbers, return all possible permutations.

For example,
[1,2,3] have the following permutations:
[1,2,3][1,3,2][2,1,3][2,3,1][3,1,2], and [3,2,1].

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

https://leetcode.com/problems/permutations-ii/

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

For example,
[1,1,2] have the following unique permutations:
[1,1,2][1,2,1], and [2,1,1].

二、解决思路:

比较直观的想法就是为每个元素维护一个数组,判断到某一时刻时各个元素是否已被使用过,选择没有使用过的元素向下一时刻递归。这种方法需要额外的空间开销,并且多次比较时间上会产生浪费。

观察排列[1,2,3] 其余排列与它的关系可视为交换对应位置元素得到的,于是可以想到用交换的方式生成全排列。

那么如何尝试所有交换并且不发生重复呢?当然是回溯(递归),但是如何高效的递归?如下

void myPermute(vector<vector<int>> &ans, vector<int>& nums, int startPos) {
	if (startPos == nums.size()-1) {
		ans.push_back(nums);
	}
	else {
		for (int i = startPos; i < nums.size(); ++i) {
			swap(nums, startPos, i);
			myPermute(ans, nums, startPos+1);
			swap(nums, startPos, i);
		}
	}
}
在myPermute()中,通过startPos限制交换的范围,每次要固定其之前的元素不变,尝试在之后的所有元素中进行交换,并且递归调用startPos+1,返回后需要将状态恢复。

swap()函数也比较简单:

void swap(vector<int>& nums, int pos1, int pos2) {
	int tmp= nums[pos2];
<span style="white-space:pre">	</span>nums[pos2] = nums[pos1];
<span style="white-space:pre">	</span>nums[pos1] = tmp;
}
也可以装一波提高效率的swap:

void swap(vector<int>& nums, int pos1, int pos2) {
	nums[pos1] ^= nums[pos2];
	nums[pos2] ^= nums[pos1];
	nums[pos1] ^= nums[pos2];
}
然而,坑点来了!这也是我写这篇文章的驱动力之一 = =

WA!为什么呢?

显然是因为装逼的原因...

注意:

1. 异或操作的两个元素如果相同的话,会产生0!

所以为了将初始排列包括进去,对myPermute()修改如下:

void myPermute(vector<vector<int>> &ans, vector<int>& nums, int startPos) {
	if (startPos == nums.size()) {
		ans.push_back(nums);
	}
	else {
	myPermute(ans, nums, startPos+1);//filter the element-self
		for (int i = startPos+1; i < nums.size(); ++i) {
			swap(nums, startPos, i);
			myPermute(ans, nums, startPos+1);
			swap(nums, startPos, i);
		}
	}
}
显然这不是一种通用的方法,因为题目I中说了没有重复元素,所以如果有重复元素,过滤也不管用啊!

2. 异或0得到元素本身:

虽然这个题目中貌似没有测到0,不过还是比较坑的...


全排列解决了,去重全排列的问题就比较简单了,只需要加上一个判断重复函数:

void myPermute(vector<vector<int>> &ans, vector<int>& nums, int startPos) {
	if (startPos == nums.size()-1) {
		ans.push_back(nums);
	}
	else {
		for (int i = startPos; i < nums.size(); ++i) {
			if (!findDuplicate(nums, startPos, i)) {
				swap(nums, startPos, i);
				myPermute(ans, nums, startPos+1);
				swap(nums, startPos, i);
			}
		}
	}
}
bool findDuplicate(vector<int> &nums, int pos1, int pos2) {
	for (; pos1 < pos2; ++pos1) {
		if (nums[pos1] == nums[pos2]) {
			return true;
		}
	}
	return false;
}
findDuplicate()保证从[pos1, pos2)之间没有与pos2相同的元素时才进行交换,如果有的话要保证nums[pos1]与其最接近的nums[pos2]进行交换,否则会产生重复。

三、源码及总结:

问题比较简单,但是是非常经典的问题,里面也能不小心从装逼失败中发现一些细节问题。下面只贴II的代码了,I只用把if判断注释掉就行了。

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<vector<int>> ans;
        sort(nums.begin(), nums.end());
        myPermute(ans, nums, 0);
        return ans;
    }
private:
	void myPermute(vector<vector<int>> &ans, vector<int>& nums, int startPos) {
		if (startPos == nums.size()-1) {
			ans.push_back(nums);
		}
		else {
			for (int i = startPos; i < nums.size(); ++i) {
				if (!findDuplicate(nums, startPos, i)) {
					swap(nums, startPos, i);
					myPermute(ans, nums, startPos+1);
					swap(nums, startPos, i);
				}
			}
		}
	}
	bool findDuplicate(vector<int> &nums, int pos1, int pos2) {
		for (; pos1 < pos2; ++pos1) {
			if (nums[pos1] == nums[pos2]) {
				return true;
			}
		}
		return false;
	}
	void swap(vector<int>& nums, int pos1, int pos2) {
		int tmp = nums[pos2];
		nums[pos2] = nums[pos1];
		nums[pos1] = tmp;
	}
};







  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值