0
点赞
收藏
分享

微信扫一扫

LeetCode带有重复元素的全排列(用时超过100%的C++提交)

小北的爹 2022-08-17 阅读 55


题目描述

LeetCode带有重复元素的全排列(用时超过100%的C++提交)_递归


从提交结果可以看到此算法效率是很高的,虽然效率高,但递归算法还是有点难理解,我给大家口述一下代码逻辑。第一次调用函数perm时算法正式开始,此perm函数只有一个if…else…结构,if就是递归函数的出口,而else就是进行递归的部分,主要是用到了分治的思想。

算法思想:

以不重复元素1,2,3为例为大家讲解算法

首先for循环区间为1,2,3,perm中的for循环开始

LeetCode带有重复元素的全排列(用时超过100%的C++提交)_数组_02


nums[i]和nums[low]交换位置,此时i与low相等,自己和自己交换位置。接着进入第二层递归,for循环区间为2,3,图如下

LeetCode带有重复元素的全排列(用时超过100%的C++提交)_递归_03


nums[i]和nums[low]交换位置,此时i与low相等,自己和自己交换位置。接着进入第三层递归,for循环区间为3,图如下

LeetCode带有重复元素的全排列(用时超过100%的C++提交)_for循环_04


此时low==high,进入if,保存此时的数组nums(即1,2,3),第三层递归执行到函数尾部,返回调用处。即上一层的perm(nums, 2, 2);

LeetCode带有重复元素的全排列(用时超过100%的C++提交)_递归_05


接着执行nums[1]和nums[2] 交换位置(即还原数组操作),此时也是自己和自己交换,然后进入下一轮for循环,执行i++(图中已完成),然后nums[low]和nums[i] 交换位置,得到如下的数组

LeetCode带有重复元素的全排列(用时超过100%的C++提交)_数组_06


接着进行下一层递归,perm(nums, low+1, high),即perm(nums, 2, 2)

LeetCode带有重复元素的全排列(用时超过100%的C++提交)_for循环_07

此时low==high,进入if,保存此时的数组nums(即1,3,2),第三层递归执行到函数尾部,返回调用处。即上一层的perm(nums, 2, 2);,接着执行交换操作swap(nums[1], nums[2]),将数组还原(变成序列1,2,3)。else语句执行完成后到达函数尾,再返回到调用处perm(nums, 1, 2),执行交换操作后 i++ 进行下一轮for循环

LeetCode带有重复元素的全排列(用时超过100%的C++提交)_数组_08


至此,元素1放在0号位置结束,执行swap(nums[low], nums[1]),将数组变成2,1,3,此时元素2放在0号位置,开始新一轮的递归

LeetCode带有重复元素的全排列(用时超过100%的C++提交)_递归_09

提示:看代码只看perm函数即可,不用把递归理解的很复杂,此函数只有一个if…else…结构

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

vector<vector<int> > res;

//检查是否已经遍历过重复元素
bool isSwap(vector<int>& nums, int low, int high){
for(int i=low; i<high; i++){
if(nums[high] == nums[i]){
return false;
}
}
return true;
}

void perm(vector<int>& nums, int low, int high){
//递归分治到最后,只剩下了最后一个元素,将此时的数组保存即可
if(low == high){
res.push_back(nums);
}else{
//检查在i遍历过的区间[low,i-1]中是否出现过此时正要交换的nums[i]
//若已经遍历过与此时的nums[i]相等的元素
//则不用交换起始元素nums[low]和当前元素nums[i],跳过本次循环
for(int i=low; i<=high; i++){
if( isSwap(nums, low, i) ){
swap(nums[low], nums[i]);
perm(nums, low+1, high);
swap(nums[low], nums[i]);
}
}
}
}

vector<vector<int> > permuteUnique(vector<int>& nums) {
perm(nums, 0, nums.size()-1);
return res;
}

int main(){
vector<int> nums;
nums.push_back(1);
nums.push_back(2);
nums.push_back(2);
nums.push_back(3);
vector<vector<int> > res = permuteUnique(nums);
for(int i=0; i<res.size(); i++){
for(int j=0; j<res[i].size(); j++){
cout<<res[i][j]<<" ";
}
cout<<"\t";
}
return 0;
}

本地运行结果:

LeetCode带有重复元素的全排列(用时超过100%的C++提交)_for循环_10


线上提交结果:

LeetCode带有重复元素的全排列(用时超过100%的C++提交)_for循环_11

总结:

递归算法确实不太好讲,也不容易理解。但是一定要把握住大的方向,那就是所有元素都有机会放在循环区间的首位置(即代码中的swap(nums[low],nums[i]),low表示此时区间的首位置),将剩余元素进行全排列。一层一层递归分治的时候,for循环区间不断减小,当区间内元素只剩一个时(即low==high),输出保存此时的数组。


举报

相关推荐

0 条评论