题目描述
给出一个不含重复数字的排列,求这些数字的所有排列按字典序排序后该排列的编号。其中,编号从1开始。
样例1:
输入:[1,2,4]
输出:1
样例2:
输入:[3,2,1]
输出:6
题目分析
本题本身是一个考察排列组合序号的算法,给出一个组合,比如1,2,4,那么它的排列组合数为:
- 1,2,4
- 1,4,2
- 2,1,4
- 2,4,1
- 4,1,2
- 4,2,1
这六种,从小到大排列,如果给出输入为1,4,2,则输出2,因为它排组合中第二大。
核心点在于——第N大,则输出N。
思路制定
既然是找本序列的位次N,那么我们来简单找一下规律——如果我给出4,1,2,你一定可以很快发现,它是第五大的序列。
为什么?
很简单,因为百位数是4,那么起码大于了百位数为1和2的情况(4种),十位为1,没有比十位为1更小的情况,个位为4,在这种情况下没有比个位为1更小的情况。
规律找到——即找到当前位置的数为第N大,则有(n-1)*(位数-1)!比它小。
比如4,是百位数,第三大,则有(3-1)*2!个数比它小。
转换伪代码
思路缺点,想转换伪代码则非常简单了。
for i :序列:
for j:剩下的序列:
计算i比多少个j大,并赋值于count
总位+=(count-1)*(位数-1)!
return 总位
话不多说,上代码!
public class Solution{
public long permutationIndex(int[] a){
long allcount=0l;//初始化总数
int n=a.length;//先转移a的length,避免多次取值耗损时间
int[] x=new int[n];
long allgroup=1l;//先计算出总的位数阶乘,每次计算后/当前位次
for(int i=1;i<n;i++){
allgroup*=i;
}
for(int i=0;i<n;i++){
int count=0;
for(int j=i+1;j<n;j++){
if(j!=n){//这里其实提前令i<n-1即可,但当时写代码太快,没太注意细节
if(a[i]>a[j]){
count++;
}
}
}
allcount+=(count*allgroup);
if(allgroup>1){
allgroup/=(n-i-1)
}
}
return allcount+1;
}
}
这套代码是过了lintcode的内存和速度要求的,也就是说,大致时间空间复杂度是ok的,不过这套算法的速度很明显是排在末尾的。问题在哪?
来看我博客的,大多是学生和菜鸟学习阶段,所以这个问题Mo交给认真阅读的同学,如果你对它有兴趣,并且发现了我编写的漏洞,可以在下面讨论,实在想不到的同学,也可以私聊我,我会详细给同学们解答。