算法设计与分析: 2-9 排列的字典序问题

2-9 排列的字典序问题


问题描述

n个元素{1,2,…,n}有n!个不同的排列。将这n!个排列按字典序排列并编号为0,1,…,n!-1。每个排列的编号为其字典序值。例如,当n=3时,6个不同排列的字典序值如下:

字典序值 0 1 2 3 4 5
排列 123 132 213 231 312 321

给定n,以及n个元素{1,2,…,n}的一个排列,计算出这个排列的字典序值,以及按字典序排列的下一个排列。


全排列

Java: version-1

import java.util.*;

public class Main {

    private static int n;
    private static String inputStr;
    private static char[] strToChar = new char[1000];

    private static String tmpPerm;

    private static Set<String> permSet = new HashSet<>();

    public static void main(String[] args) {

        Scanner input = new Scanner(System.in);

        while(true){
            permSet.clear();

            System.out.println("Please input the length of string: ");
            n = input.nextInt();
            System.out.println("Please input a digit string: ");
            inputStr = input.next();
            System.out.println("-------------------------");
            strToChar = inputStr.toCharArray();

            permutate(strToChar, 0, n-1);

            int i=0;
            int size = permSet.size();
            String[] numbers = new String[size];
            Iterator<String> iterator = permSet.iterator();
            while (iterator.hasNext()){
                String value = iterator.next();
                numbers[i++] = value;
            }

            Arrays.sort(numbers);
            for(i=0; i<size; i++){
                if(numbers[i].equals(inputStr)) {
                    System.out.println("Permutation at: "+i);
                    if(i+1 < size)
                        System.out.println("Next permutation: "+numbers[i+1]);
                    else
                        System.out.println("There is no permutation after: "+numbers[i]);
                }
            }

            System.out.println("------------------------------------------");
        }
    }

    private static void permutate(char[] charList, int k, int m){
        if(k == m){
            tmpPerm = String.valueOf(charList);
            permSet.add(tmpPerm);
            return;
        }

        for(int i=k; i<=m; i++){
            swap(charList, k, i);
            permutate(charList, k+1, m);
            swap(charList, k, i);
        }
    }

    private static void swap(char[] charList, int k, int i){
        char temp;
        temp = charList[k];
        charList[k] = charList[i];
        charList[i] = temp;
    }
}

Input & Output

Please input the length of string: 
8
Please input a digit string: 
26458173
-------------------------
Permutation at: 8227
Next permutation: 26458317
------------------------------------------
Please input the length of string: 
8
Please input a digit string: 
87654321
-------------------------
Permutation at: 40319
There is no permutation after: 87654321
------------------------------------------
Please input the length of string: 
8
Please input a digit string: 
87654312
-------------------------
Permutation at: 40318
Next permutation: 87654321
------------------------------------------
Please input the length of string: 
6
Please input a digit string: 
231456
-------------------------
Permutation at: 144
Next permutation: 231465
------------------------------------------
Please input the length of string: 

数学推导

Java: version-2

import java.util.Scanner;

public class Main{

    private static boolean hasNextPerm = true;

    public static void main(String[] args){

        int n;

        Scanner input = new Scanner(System.in);

        while(true){

            System.out.println("Input the number of digits:");
            n = input.nextInt();

            int[] numbers = new int[n];

            System.out.println("Input digits:");
            for(int i=0; i<n; i++){
                numbers[i] = input.nextInt();
            }

            int rank = dictRank(numbers, n);

            nextPermutation(numbers, n);

            System.out.println("Permutation at: "+rank);

            if(hasNextPerm) {
                System.out.print("Next Permutation: ");
            }
            else
                System.out.print("There is no permutation after: ");

            for (int i = 0; i < n; i++)
                System.out.print(numbers[i]);

            System.out.println();
            System.out.println("-------------------------------------");
        }

    }

    /*
    如何得到2 6 4 5 8 1 7 3的下一个排列?
    1 从尾部往前找第一个P(i-1) < P(i)的位置
            2 6 4 4 5 8 1 <-- 7 <-- 3
        最终找到1是第一个变小的数字,记录下1的位置i-1
    2 从尾部往前找到第一个大于1的数
            2 6 4 4 5 8 1 7 3 <--
        最终找到3的位置,记录位置为m
    3 交换位置i-1和m的值
            2 6 4 4 5 8 3 7 1
    4 倒序i位置后的所有数据
            2 6 4 4 5 8 3 1 7
    */
    //下一个排列
    private static void nextPermutation(int[] numbers, int n){
        int min=0, max=n-1, i, j;
        for(i=n-1; i>0; i--){ //1
            if(numbers[i-1] < numbers[i]){
                min = i-1;
                break;
            }
        }
        if(i == 0)
            hasNextPerm = false;
        else{

            hasNextPerm = true;
            for(j=n-1; j>=0; j--){ //2
                if(numbers[j] > numbers[min]){
                    max = j;
                    break;
                }
            }

            swap(numbers, min, max); //3

            reverse(numbers, min+1, n-1); //4
        }
    }

    //倒序
    private static void reverse(int[] numbers, int from, int to){
        int[] tmp = new int[to+1];
        for(int h=from; h<=to; h++)
            tmp[h] = numbers[h];

        for(int h=from; h<=to; h++)
            numbers[h] = tmp[to+from-h];
    }

    //交换
    private static void swap(int[] numbers, int min, int max){
        int tmp = numbers[min];
        numbers[min] = numbers[max];
        numbers[max] = tmp;
    }

    /*
    2 6 4 5 8 1 7 3
    total=0;
    比2小的数有1个,则 total+=1*7!;
    比6小的数有4个,则 total+=4*6!;
    比4小的数有2个,则 total+=2*5!;
    比5小的数有2个,则 total+=2*4!;
    比8小的数有3个,则 total+=3*3!;
    比1小的数有0个,则 total+=0*2!;
    比7小的数有1个,则 total+=1*1!;
    比3小的数没有;
    */
    //第一种字典序算法
    private static int dictRank(int[] numbers, int n){
        int count;
        int total = 0;
        for(int i=0; i<n-1; i++){
            count = 0;
            for(int j=i+1; j<n; j++)
                if(numbers[j] < numbers[i])
                    count++;
            total += count*factorial(n-i-1);
        }

        return total;
    }

    //第二种字典序算法
    private static int dictRank2(int[] numbers, int n){
        int count;
        int sum = 0;

        for(int i=0; i<n-1; i++){
            count = numbers[i] - 1;
            for(int j=0; j<i; j++)
                if(numbers[j] < numbers[i])
                    count--;
            sum += count*factorial(n-i-1);
        }

        return sum;
    }

    //n的阶乘
    private static int factorial(int n){
        int total = 1;
        for(int i=1; i<=n; i++){
            total *= i;
        }

        return total;
    }
}

Input & Output

Input the number of digits:
8
Input digits:
2 6 4 5 8 1 7 3
Permutation at: 8227
Next Permutation: 26458317
-------------------------------------
Input the number of digits:
8
Input digits:
8 7 6 5 4 3 2 1
Permutation at: 40319
There is no permutation after: 87654321
-------------------------------------
Input the number of digits:
8
Input digits:
8 7 6 5 4 3 1 2
Permutation at: 40318
Next Permutation: 87654321
-------------------------------------
Input the number of digits:
6
Input digits:
2 3 1 4 5 6
Permutation at: 144
Next Permutation: 231465
-------------------------------------
Input the number of digits:

Reference

王晓东《计算机算法设计与分析》(第3版)P45

猜你喜欢

转载自blog.csdn.net/ioio_/article/details/80975927
2-9