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

来源:互联网 发布:公务员 知乎 编辑:程序博客网 时间:2024/06/06 01:33

一、问题描述:

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

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-selffor (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 0
原创粉丝点击