// 求N个数的全排列
#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#include <string>
using namespace std;
#define N 5
int n = 0;
void output(int str[N], int begin)
{
if (begin == N - 1)
{
for (int i = 0; i < N; i++)
{
cout<<str[i]<<"\t";
}
cout<<endl;
n++;
return;
}
for (int p = begin; p < N; p++)
{
int t = str[p];
str[p] = str[begin];
str[begin] = t;
output(str, begin+1);
t = str[p];
str[p] = str[begin];
str[begin] = t;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int a[N],i;
for (i = 0; i < N; i++)
{
a[i] = i+1;
}
cout<<"所有的排序:"<<endl;
output(a, 0);
cout<<endl<<"共有 "<<n<<"种"<<endl;
system("pause");
return 0;
}
这是递归算法,相应的时间复杂度和空间复杂度会很大,下面是非递归算法:
要考虑全排列的非递归实现,先来考虑如何计算字符串的下一个排列。如"1234"的下一个排列就是"1243"。只要对字符串反复求出下一个排列,全排列的也就迎刃而解了。
如何计算字符串的下一个排列了?来考虑"926520"这个字符串,我们从后向前找第一双相邻的递增数字,"20"、"52"都是非递增的,"26 "即满足要求,称前一个数字2为替换
数,替换数的下标称为替换点,再从后面找一个比替换数大的最小数(这个数必然存在),0、2都不行,5可以,将5和2交换得到"956220",然后再将替换点后的字符"6220"颠
倒即得到"950226"。对于像"4321"这种已经是最“大”的排列,采用STL中的处理方法,将字符串整个颠倒得到最“小”的排列"1234"并返回false。这样,只要一个循环再加上计算
字符串下一个排列的函数就可以轻松的实现非递归的全排列算法。按上面思路并参考STL中的实现源码,不难写成一份质量较高的代码。值得注意的是在循环前要对字符串排序
下!
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
//交换数组a中下标为i和j的两个元素的值
void swap(int* a,int i,int j)
{
a[i]^=a[j];
a[j]^=a[i];
a[i]^=a[j];
}
//将数组a中的下标i到下标j之间的所有元素逆序倒置
void reverse(int a[],int i,int j)
{
while (i < j)
swap(a,i++,j--);
}
void print(int a[],int length)
{
for(int i=0;i<length;++i)
cout<<a[i]<<" ";
cout<<endl;
}
//求取全排列,打印结果
void combination(int a[],int length)
{
if(length<2) return;
while(true)
{
print(a,length);
int i,j;
for(i=length-2;i>=0;--i) //找升序的相邻2数,前一个数即替换数
{
if(a[i]<a[i+1]) break;
else if(i==0) return;
}
for(j=length-1;j>i;--j) //从后往前找到替换点后第一个比替换数大的数
if(a[j]>a[i]) break;
swap(a,i,j);
reverse(a,i+1,length-1); //将替换点以后的序列反转
}
}
int QsortCmp(const void *pa, const void *pb)
{
return *(char*)pa - *(char*)pb;
}
int main()
{
int arr[5] = {1,2,3,4,5};
qsort(arr, 5, sizeof(arr[0]), QsortCmp); //排序之前先将数组递增排序
combination(arr, 5);
}