java实现排程算法_康托展开算法和逆康托展开算法[Java实现]

本文介绍了如何使用Java实现康托展开和逆康托展开算法,以解决数组全排列问题。通过示例解释算法过程,并提供Java源码进行辅助理解。

基于这篇介绍我实现了基于Java的算法

都能看懂的康托展开_ltrbless的博客-优快云博客​blog.youkuaiyun.com
860446ba93089fd7d9ec15ec466037a4.png

条件

一个数组:[1, 2, 3, 4, 5]

康托展开算法

找出 52413 是这个数组的全排列中的第几个

算法详解:

5 作为第一个数,比它小的数有四个,5 的后面有4个位置可待排序使用,则这四个数的全排列有 4 * 4! 种

2 作为第二个数,比它小的数只有一个,2 后面只有3个位置可用来做全排列使用,全排列的个数是 1 * 3!

4 作为第三个数,比它小的数有3个,但是2已经使用过了,所以只能用2个,全排列的个数是 2 * 2!

同理可得 1 有 0 * 1!种,3 有 0 * 0!种

把上面提到的全排列的种数相加就得到了 106 
因为这种算法是从 0 开始计算的,所以 52413 是第 106+1 个排列

Java源码

 /**
     * 输入一个整数,返回它是该数组全排列里面的第几个数
     *
     * @param n
     * @return
     */
    public static int cantorExpansion(int n) {
        int position = 0;
        String number = String.valueOf(n);
        for (int i = 0; i < number.length(); i++) {
            position += (getMinCount(number.charAt(i), number, i) * getFactorial(number.length()-i-1));
        }
        return position + 1;
    }

    // 求小于等于 x 的数有几个,且排除已经处理过的数
    public static int getMinCount(char x, String number, int index) {
        Integer num = Integer.valueOf(String.valueOf(x));
        for (int i = 0; i < index; i++) {
            Integer tmp = Integer.valueOf(String.valueOf(number.charAt(i)));
            if (tmp < num) {
                num--;
            }
        }
        return num - 1;
    }

    // 求x的阶乘
    public static int getFactorial(int x) {
        if (x == 0)
            return 1;
        int res = x;
        for (int i = 1; i < x; i++) {
            res *= i;
        }
        return res;
    }



    public static void main(String[] args) {
        System.out.println(cantorExpansion(52413)); // 107
        System.out.println(cantorExpansion(312)); // 5
        System.out.println(cantorExpansion(34125)); // 61
    }

逆康托算法

该数组的全排列中的第 107 个排列是什么数找到

算法详解:n = 5, k = 107

上面提到过,康托展开是从 0 开始计算的,所以求 107 个排列数的时候应该求的是 106

12345 作为第一个排列数,每一个数字可以排列的次数是 4!,3!,2!,1!,0!

用 106 / 24 = 4 ... 10 可以得到第106个排列的第一个数字应该是 array[4] = 5

第一个数找到之后更新 k 为上一次计算的余数,同时把上一次在数组中找到的数删除,重复以上过程

最终得到一个数字序列,该序列就是第 107 个排列对应的数据

Java源码

public static void main(String[] args) {
        System.out.println(inverseCantorExpansion(5, 107));//52413
        System.out.println(inverseCantorExpansion(8, 27));// 12354768
    }

    /**
     * 给出 [1,2,3,...,n] 数组以及该数组全排列的第 K 个序列编号,求第 K 个数是啥
     *
     * @param n 数组的最大值
     * @param k 序号
     * @return 第K个数的值
     */
    public static String inverseCantorExpansion(int n, int k) {
        // 先搞一个1~n的数组,用来在里面选择合适的数字加入 ans 中
        ArrayList<Integer> array = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            array.add(i + 1);
        }

        StringBuilder ans = new StringBuilder();
        k -= 1;

        // 执行 n 次循环得到 n 个数,组合成一个答案
        for (int i = 0; i < n; i++) {
            // 计算从 (n-1)! 开始的阶乘
            int Xfactorial = getFactorial(n - i - 1);
            // 计算获取数组的那一个位置的数字
            int index = k / Xfactorial;
            // 获取该数字
            ans.append(array.get(index));
            // 更新 k 为上一次除法计算的余数
            k %= Xfactorial;
            // 同时更新数组,删除已经获得过的数字
            array.remove(index);
        }
        return ans.toString();
    }

    // 求x的阶乘
    public static int getFactorial(int x) {
        if (x == 0)
            return 1;
        int res = x;
        for (int i = 1; i < x; i++) {
            res *= i;
        }
        return res;
    }

本文算法文字解释很简洁,看不明白的话可以看我开头引用的优快云博客的介绍。

看完他的介绍再看我的就十分清晰了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值