LintCode388:第k个排列

题目:给定 n 和 k,求123..n组成的排列中的第 k 个排列。


分析:

首先考虑能不能确定第k个排列是以哪个数字开头,以[1,2,3,4]的全排列为例,找第14个排列

  • 以1开头的排列总共有3!个,原因是第一个位置是1,剩下3个位置可以随便排列,有6个
  • 以2开头的排列总共有3!个,原因是第一个位置是2,剩下3个位置可以随便排列,有6个
  • 此时已经有12个排列
  • 所以剩下的两个排列即第14个排列一定在以3开头的排列中

用这种方式继续缩减数量,以3开头的排列中最小的为[3,1,2,4],3已经固定,那么就找[1,2,4]的全排列的第2个排列,就是整个排列的第14个排列

  • 以1开头的排列共有2!个,原因是第二个位置是1,剩下2个位置可以随便排列,有2个
  • 此时已经有两个排列,可以确定结果一定在以[3,1]开头的排列中,即[3,1,2,4][3,1,4,2]

继续缩减数量,以[3,1]开头的排列中最下的为[3,1,2,4][3,1]已经固定,那么就找[2,4]的全排列的第2个排列,就是[1,2,4]的全排列的第2个排列,也就是整个排列的第14个排列

  • 以2开头的排列共有1!个,原因是第三个位置是2,剩下一个位置给4,有1个
  • 以4开头的排列共有1!个,原因是第三个位置是4,剩下一个位置给2,有1个
  • 此时已经有两个排列,可以确定结果是以4开头的排列,即[4,2],所以结果为[3,1,4,2]

所以,可以每次确定一个大范围,在大范围的基础上进一步缩小范围,直到最后只有一个数字为止。遍历n遍即可。

假设某次需要找到第k个排列(k从0开始),以第i个位置开头(i从0开始),上述过程可以表示为要找的排列的开头是所剩数字中的第k / (n-i-1)!个 数字。(从0开始)

上述第一步,序列为[1,2,3,4],k为13,i为0,(n-i-1)!为6,k / (n-i-1)!为2,即第2个数字(从0开始),为3 ,k需要更新,k%(n-i-1)!

上述第二步,序列为[1,2,4],确定以3开头后,k为1,i为2,(n-i)!为2,k / (n-i)!为0,即第0个数字,为1。

package Array;

public class getPermutation {
    /**
     * @param n: n
     * @param k: the k th permutation
     * @return: return the k-th permutation
     */
    //给定 n 和 k,求123..n组成的排列中的第 k 个排列。
    public String getPermutation(int n, int k) {
        // write your code here
        StringBuffer sb=new StringBuffer();
        boolean[] isused=new boolean[n];
        int factor=1;
        for(int i=1;i<n;i++){
            factor*=i;
        }
        k=k-1;//从0开始
        for(int i=0;i<n;i++){
            int index=k/factor;  
            k=k%factor;
            for(int j=0;j<n;j++){
                if(!isused[j]){
                    if(index==0){
                        isused[j]=true;
                        sb.append((char)('0'+j+1));
                        break;
                    }else{
                        index--;
                    }
                }
            }
            if(i<n-1){
                factor/=n-i-1;
            }
        }
        return sb.toString();
    }
}

猜你喜欢

转载自blog.csdn.net/qq_27139155/article/details/80758184