排列的字典序问题

from: http://www.cnblogs.com/janqii/archive/2010/04/08/1722499.html

Problem D:【算法】:排列的字典序问题

Time Limit:2000MS Memory Limit:65536K 
Total Submit:200 Accepted:66

Description

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

任务:给定n 以及n 个元素{1,2,..., n }的一个排列,计算出这个排列的字典序值,以及按字典序排列的下一个排列。 
Input

第1 行是元素个数n(n < 15)。接下来的1 行是n个元素{1,2,..., n }的一个排列。

Output

第一行是字典序值,第2行是按字典序排列的下一个排列。

Sample Input 

2 6 4 5 8 1 7 3

Sample Output 
8227 
2 6 4 5 8 3 1 7 
-----------------------------------------------------------------------------------------------------------------------

字典序法:
字典序法中,对于数字1、2、3......n的排列,不同排列的先后关系是从左到右逐个比较对应的数字的先后来决定的。例如对于5个数字的排列 12354和12345,排列12345在前,排列12354在后。按照这样的规定,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 
4)再将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>

void dict_sort(int arr[],int size);
int find_first_small(int arr[],int size);
int find_smallest(int arr[],int start,int size);
void swap(int arr[],int idx1,int idx2);
void converse(int arr[],int start,int end);
int factorial(int n);
int find_position(int arr[],int size,int *pos);

int main(void)
{
int i,n,val;
int pos;
int *a;

scanf("%d",&n);
a=(int*)malloc(n*sizeof(int));

for(i=0;i<n;i++)
{
   scanf("%d",&a[i]);
   //a[i]=val;
}

dict_sort(a,n);
find_position(a,n,&pos);

printf("%d\n",pos);
for(i=0;i<n;i++)
   printf("%d ",a[i]);

return 0;
}

void dict_sort(int arr[],int size)
{

int idx1;
int idx2;

idx1=find_first_small(arr,size);

if(idx1==-1)

   printf("the last one...\n");
   return ;
}

idx2=find_smallest(arr,idx1+1,size);
swap(arr,idx1,idx2);
converse(arr,idx1+1,size-1);
}

int find_position(int arr[],int size,int *pos)
{
int i,nr;
int sum=0;
for(i=0;i<size-1;i++)
{
   nr=get_counter(arr,i,size);

   if(nr>0)
    sum += nr*factorial(size-i-1);
}
*pos=sum+1;

return *pos;
}

int factorial(int n)
{
int rt=1;
while(n>0)
{
   rt *=n;
   n--;
}
}

int find_first_small(int arr[],int size)
{
int i=size-1;

for(i=size-1;i>=0;i--)
{
   if(arr[i-1]<arr[i])
    return i-1;
}

return -1;
}

int get_counter(int arr[],int start,int size)
{
int i;
int counter=0;
int start_val=arr[start];

for(i=start+1;i<size;i++)
{
   if(arr[i]<start_val)
    counter++;
}

return counter;
}

int find_smallest(int arr[],int start,int size)
{
int i;
int pos=start;
int start_val=arr[start-1];
int key=arr[start];

for(i=start+1;i<size;i++)
{
   if(arr[i]>start_val&&arr[i]<key)
   {
    key=arr[i];
    pos=i;
   }

}

return pos;
}

void swap(int arr[],int idx1,int idx2)
{
int tmp=arr[idx1];

arr[idx1]=arr[idx2];
arr[idx2]=tmp;
}

void converse(int arr[],int start,int end)
{
int i=end;

for(i=end;i>=(start+end+1)/2;i--)
{
   swap(arr,end-i+start,i);
}
}

排列字典序问题通常涉及计算给定排列字典序值以及找出按字典序排列的下一个排列等。 从引用中可知,常见的全排列问题算法教材上有perm1和perm2两种算法,但原有的全排列算法无法按字典序输出,可在每一次递归前加入对即将进行操作部分的排序操作来解决按字典序输出的问题 [^3]。 对于计算给定排列字典序值以及找出下一个排列的编程任务,虽然引用中未给出完整代码,但提供了一些思路和部分代码示例。例如,在求字符串排列时,可使用如下代码: ```cpp #include <iostream> #include <vector> #include <set> #include <algorithm> #include <string> class Solution { public: void perm(int pos, std::string s, std::set<std::string> &ret) { if (pos + 1 == s.length()) { ret.insert(s); return; } for (int i = pos; i < s.length(); ++i) { std::swap(s[pos], s[i]); perm(pos + 1, s, ret); std::swap(s[pos], s[i]); } } std::vector<std::string> Permutation(std::string s) { if (s.empty()) return {}; std::set<std::string> ret; perm(0, s, ret); return std::vector<std::string>({ret.begin(), ret.end()}); } }; ``` 此代码通过递归和回溯的方法生成字符串的所有排列存储在`set`中以保证按字典序存储,最后将其转换为`vector`返回 [^5]。 若要解决给定`n`以及`n`个元素`{1, 2, …, n}`的一个排列,计算其字典序值和下一个排列问题,可按以下思路实现: ```cpp #include <iostream> #include <vector> #include <algorithm> // 计算排列字典序值 int getLexicographicalOrder(const std::vector<int>& perm) { int n = perm.size(); int order = 0; for (int i = 0; i < n; ++i) { int count = 0; for (int j = i + 1; j < n; ++j) { if (perm[j] < perm[i]) { ++count; } } int factorial = 1; for (int k = 1; k < n - i; ++k) { factorial *= k; } order += count * factorial; } return order; } // 计算下一个排列 std::vector<int> getNextPermutation(std::vector<int> perm) { int n = perm.size(); int i = n - 2; while (i >= 0 && perm[i] >= perm[i + 1]) { --i; } if (i >= 0) { int j = n - 1; while (j > i && perm[j] <= perm[i]) { --j; } std::swap(perm[i], perm[j]); } std::reverse(perm.begin() + i + 1, perm.end()); return perm; } int main() { std::vector<int> perm = {1, 2, 3}; int order = getLexicographicalOrder(perm); std::cout << "字典序值: " << order << std::endl; std::vector<int> nextPerm = getNextPermutation(perm); std::cout << "下一个排列: "; for (int num : nextPerm) { std::cout << num; } std::cout << std::endl; return 0; } ``` 上述代码中,`getLexicographicalOrder`函数用于计算给定排列字典序值,`getNextPermutation`函数用于计算下一个排列
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值