算法学习日记 字典法排序
问题:给出给定序列的全排列
例如:序列为 1 2 3
输出为 123 132 213 231 312 321
问题分析:
(1)如何对一组数找到他的全排列
解答:采用字典序排列算法。用字典序法得到全排列的思路大概是这样的:我们需要有一个初始的排列状态,对于这个排列,用字典序法转换就得到下一个排列。我们把初始状态设置为字符从小到大地排,不断地用字典序法得到它的下一个排列,直到最后一个排列为止,而最后一个排列就是字符从大到小地排喽。比如说5个数字的所有的排列中最前面的是12345,最后面的是54321。
具体算法如下
设P是1~n的一个全排列:p=p1p2......pn=p1p2......pj-1pjpj+1......pk-1pkpk+1......pn
(1)从排列的右端开始,找出第一个比右边数字小的数字的序号j(j从左端开始计算),即 j=max{i|pi<pi+1}
(2)在pj的右边的数字中,找出所有比pj大的数中最小的数字pk,即 k=max{i|pi>pj}(右边的数从右至左是递增的,因此k是所有大于pj的数字中序号最大者)
(3)对换pi,pk,再将pj+1......pk-1pkpk+1pn倒转得到排列p’’=p1p2.....pj-1pjpn.....pk+1pkpk-1.....pj+1,这就是排列p的下一个下一个排列。
例如839647521是数字1~9的一个排列。从它生成下一个排列的步骤如下:
自右至左找出排列中第一个比右边数字小的数字4 839647521
在该数字后的数字中找出比4大的数中最小的一个5 839647521
将5与4交换 839657421
将7421倒转 839651247
所以839647521的下一个排列是839651247。
源码如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define array_max 5
void invert_array(int *begin,int *end)
{
int temp;
while(begin<end)
{
temp=*begin;
*begin=*end;
*end=temp;
++begin;
--end;
}
}
void print(int *a)
{
int i;
for(i=0;i<array_max;i++)
{
printf("%d ",a[i]);
}
printf("\n");
}
int main(void)
{
int list[5]={2,3,1,4,5};
int min=0,point=0;
int temp=0;
int *begin=NULL;
int *end=list+array_max-1;
int i=0,j=0;
print(list);
while(1)
{
i=array_max-2;
while(i>=0&&list[i]>list[i+1])
i--;
if(i<0) break;
point=i;
begin=&list[i+1];
min=1000;
for(j=i;j<array_max;j++)
{
if((list[j]>list[i])&&(list[j]<min))
{
min=list[j];
point=j;
}
}
temp=list[i];
list[i]=list[point];
list[point]=temp;
invert_array(begin,end);
print(list);
}
return 0;
}