[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;}};
- [LeetCode]Permutation全排列和去重全排列
- leetcode之全排列(Permutation)
- leetcode-全排列(permutation)
- 字符全排列Permutation
- 全排列permutation
- Permutation全排列算法
- [LeetCode]Permutation I排列
- [LeetCode]—Next Permutation (全排列字典序)
- Leetcode #31. Next Permutation 下一个全排列 解题报告
- leetcode 60. Permutation Sequence 第K个全排列
- 全排列和去重全排列---递归实现
- LeetCode(Permutation Sequence)输出全排列中第k个排列
- LeetCode | Permutation Sequence(找到全排列中的第k个排列)
- 排列、组合和全排列
- leetcode 全排列系列
- Leetcode全排列问题
- 关于全排列 leetcode
- LeetCode--Permutations 全排列
- iOS UIStepper加减按钮
- 2016/04/19
- 数据库中获得当前系统时间戳,距离1970年1月1日秒数
- 2016/4/20
- 利用MAVEN打包时,如何包含更多的资源文件
- [LeetCode]Permutation全排列和去重全排列
- Ubuntu 12.04 / Linux Mint 17.1 下安装TinyOS-2.1.1全过程
- Object-C 协议
- test03
- 科学美国人|Bird Combines Calls in Specific Order
- NGINX 使用
- Glib翻译
- Timus 1298. Knight
- linux自学笔记(1)