排列序号

排列序号一

给出一个不含重复数字的排列,求这些数字的所有排列按字典序排序后该排列的编号。其中,编号从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开始。

样例

给出排列[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;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值