算法分析与设计之排列的字典序问题

问题描述:n个元素{1,2,3...n}有n!种不同的排列。将这n!个排列按字典序排列,并编号0,1,2,3...n!-1。每个排列的编号为其字典序值。例如,当n=3时,有6个不同排列的字典序如下:

字典序值 0 1 2 3 4 5
排列 123 132 213 231 312 321
算法设计:给定n以及n各元素{1,2,3...n}的一个排列,计算出这个排列的字典序值,以及按字典序排列的下一个排列。

问题分析:比较简单的想法就是就是将n个元素进行排列有n!中组合,然后将这些数进行排序。但是这样存在,效率低下、存储空间消耗巨大的问题。如果运用递归和分治策略来解决问题就会简单的多。

具体步骤:为了求排列对应的序号,只要该序列到第一个序列即0,1,2,3…n-1所需要移动的次数。移动原则是a[i]从大到小移动,对于每一个数字a[i],若i前面比a[i]小的个数正好是a[i]-1个,则这个数不需要向后移动以达到目标序列,否则i后面必然有a[i]-b[i]-1个比a[i]小的数,只要把a[i]移动到比自己小的数后面才能使得移动中的序列正向目标前进。因此只要求出每个数的移动次数,然后相加就是该序列的位置。即每个数到正确位置需要移动的次数为(n-i-1)!*(a[i]-b[i]-1)。

程序代码:

#include
#include
void SWAP(int * a, int * b)
{
    long long t;
    t = *a;
    *a = *b;
    *b = t;
}
int main()
{
    int n,i,k,j,t,order[100];
    int lis,f[100],mid,h;
    f[0]=1;
    for(i=1;i<=22;i++)
        f[i]=f[i-1]*i;
	printf("请输入元素的个数,以及一个序列\n");
    if(scanf("%d",&n)!=EOF)
    {
        for(i=0;i=2)
        {
            lis=0;
            for(i=0,k=n-1;i= 0; i--)
            {
                if(order[i] < order[i+1])
                {
                    j = i;
                    for(k = n-1; k > j; k--)
                    {
                        if(order[k] > order[j])
                        {
                            mid = j+(n-j)/2;
                            SWAP(&order[j], &order[k]);
                            for(j++, h = 1; j <= mid; j++, h++)
                                SWAP(&order[j], &order[n-h]);
                        }
                    }
                    break;
                }
            }
            printf("该序列的下一个序列是:\n");
            for(i=0; i < n-1; i++)
                printf("%d ",order[i]);
            printf("%d\n",order[i]);
        }
    }
    return 0;
}
运行截图:

算法分析与设计之排列的字典序问题_第1张图片

总结:
在排列的字典序问题上,在求某个序列是字典序的第几个时,要掌握规律,即(n-i-1)!*(a[i]-b[i]-1)。某个序列移动到最小序列即1234…n时所移动的次数即该序列所在位置,而移动次数又可以转换为每个位置上的数移动到正确位置的次数的和。由此即可求出某个序列在字典序的什么位置。在求解序列的下一个序列时,其核心思想是从数组尾部开始找相邻两个元素,满足order[i]i),交换order[i]和order[k],order[i+1]~order[n-1]进行逆向重排。

你可能感兴趣的:(算法分析,递归,分治,排列的字典序)