排列序号一
给出一个不含重复数字的排列,求这些数字的所有排列按字典序排序后该排列的编号。其中,编号从1开始。
例如,排列[1,2,4]是第1个排列。
思路:
1、由n个不重复数字刚才的排列有 n! 种;
2、对所给排列从右到左(即从低位到高位)进行分析,下标i从0开始, 第i位的数字和其右边的某一个数字交换后,对于新的i-1,i-2,...0位上的数字可以组成i!种不同的排列。这样可以将n!作为第n位的权值。
3、如果第 i 位的右边有low个比它小的数,那么将这low个数和第 i 位的数交换之后得到的排列(i,i-1,...0)在字典序中的编号肯定比交换之前的要小。又交换之后的不同排列有 i !种,则对于第i位其对序号的增量为 i ! * low。
总结一下,从低位到高位,计算其中每一位数和其右边的低位所组成的子排列进行该位和其低位某一位数交换后得到的所有可能的编号比它小的排列数目。要得到编号比它小的排列,而且只能让这一位数和在其低位上的(比它小的)数得到,这样可以使得对每一位进行讨论时,都是不相关的,即没有重复排列出现。对每一位数分析得到的增量累加可得到所有序号比所给排列小的数目,再加一即可得到结果。
代码:
public class Solution {
/**
* @param A an integer array
* @return a long integer
*/
public long permutationIndex(int[] A) {
// Write your code here
long R=0;
int low[] = new int[A.length];
long weight[] = new long[A.length];
for(int i=A.length-1;i>=0;--i){
for(int j=i+1;j<A.length;++j)
if(A[j]<A[i])
++low[i];
weight[i] = (i>=A.length-2)?(A.length-1-i):weight[i+1]*(A.length-i-1);
R += weight[i]*low[i];
}
return R+1;
}
}
参考:
http://www.geekviewpoint.com/java/numbers/permutation_index
排列序号II
样例
给出排列[1, 4, 2, 2],其编号为3。
题目分析:这题和前面一题的区别为中间有重复数字出现,则产生的变化为每一位的权值变了。不再是阶乘,必须在原有阶乘的基础上除去交换之后所得右边的子排列的所有重复数字的数目的阶乘。
代码:
public class Solution {
/**
* @param A an integer array
* @return a long integer
*/
public long permutationIndexII(int[] A) {
// Write your code here
long R=0;
long weight[] = new long[A.length];
Map<Integer, Integer> M = new HashMap();
for(int i=A.length-1;i>=0;--i){
if(i<A.length-1)
M.put(A[i+1], M.containsKey(A[i+1])?M.get(A[i+1])+1:1 );
weight[i] = (i>=A.length-2)?(A.length-1-i):weight[i+1]*(A.length-1-i);
int div=1,low=0;
for(int each:M.keySet()){
if(A[i]>each)
low += M.get(each);
int t = M.get(each);
if(each==A[i])
++t;
while(t>1){
div *= t;
--t;
}
}
R += low*weight[i]/div;
}
return R+1;
}
}