之前写过一个全排列没有重复的递归代码,然后花了一点时间研究了一些全排列去重的方法,总结出了这一个算法。之前的有个博客是下一个排列,就是从这个方法分离而出的。
下一个排列
这个方法的思路是这样的,全排列其实也可以看做一个从小到大的排列,其实就是看做从最小一下一下变大,直到最大。
举个例子更容易理解:
12345是第一个数,那么下一个排列就是12354,是所有全排列从小到大排起来,他的下一个数,再下一个就是12435,12453,12534……
那么规律是什么,我们模拟一个变化过程。
12354->12435,我们发现是把百位的3换成了4,那么为什么换成4不换成5,因为需要一步步增大,也就是交换他后面比他大的数里面最小的数,交换3、4后,12354->12453,百位变大后,数就已经变大,所以把百位之后的排成最小数列。然后递归上面的思路,一点点继续换。
这里面有个思路就是,因为我们是找到下一个要换的可以变大的位数,也就是从右往左。还是举例子15432,个位往后没有比他大的,所以找不到可以换上来的,十百千位都是,到万位,后面有比他大的数,也就是说可以有更大的数。整理思路,就是右边有比它大的数的时候,转换成代码思路就是从右往左遍历,第一个比右边数小的。同样我们也就知道,找到的这个位置后面的数列是递增的,因为我们是一步步找过来的,如果不是,到不了现在的位置。然后找后面最小的比此位置大的数字,交换,因为后面是递增的,即使交换后也是,所以我们将后面的字串翻转即可。
class Solution {
public:
vector<string> Permutation(string str) {
vector<string> res;
int len=str.length();
if(len==0)
return res;
else if(len==1)
{
res.push_back(str);
return res;
}
vector<char> tmp(len);//vector只有在声明大小的时候,才能直接拿下标赋值,否则只能push_back(这玩意儿困扰了我好久,还是基础不牢固啊,对vector理解不深)
for(int i=0;i<len;i++)
{
tmp[i]=str[i];
}
sort(tmp.begin(),tmp.end());//最小的序列
for(int i=0;i<len;i++)
{
str[i]=tmp[i];
}
res.push_back(str);
int left,right;//left记录需要换的位置,right记录后面比left大里最小的位置
while(1)
{
left=-1;
for(int i=len-1;i>=1;i--)//寻找可换的数
{
if(str[i-1]<str[i])
{
left=i-1;//记录位置
break;
}
}
if(left==-1)//如果找不到,说明已经是最大,循环结束
{
break;
}
right=left+1;
for(int j=left+2;j<len;j++)
{
if(str[j]<=str[right]&&str[j]>str[left])//寻找后面比left位置大的最小数
{
right=j;
}
}
swap(str[left],str[right]);//交换
for(int i=left+1,j=len-1;i<j;i++,j--)//翻转
{
swap(str[i],str[j]);
}
res.push_back(str);
}
return res;
}
};
看来需要学点做动画的软件了,动画演示思路更直观点。