字典序算法

原文地址:排列的算法(一)——字典序算法

1. 排列算法

从n个不同元素中取出n个元素的排列,称为n个不同元素的全排列
可以证明,n个元素的全排列的总数是n!
全排列的生成算法就是对于给定的元素集合,用有效的方法将所有可能的全排列无重复无遗漏地列举出来。

n个不同元素的排列都可以与n个自然数1、2、……,n的排列一一对应,所以,这里就以n个数字的排列为例说明排列的生成算法。
全排列的生成算法就是讨论怎样从一个排列生成它一个新的排列的方法。

2. 字典序算法

不同的排列,可以从左到右逐个比较对应的元素的顺序来决定他们的先后顺序。
设有集合{1、2、……n-1、n}的两个排列[ a0a1 ……aj……an-1]和 [b1b2……bj……bn-1bn],从左端开始,逐个比较它们的对应元素的大小。
如果出现对应数字不相同的第一个位置为 j,并且aj<bj,则[a0a1……aj……an-1]就排在[b0b1……bj……bn-1]的前面。

例如,对于5个数字的排列1235412345,两个排列的前三个数字相同,出现不同数字的位置是 j=3,而排列12354的第四个数字5在排列12345的第四个数字4之后,所以排列12345在前,排列12354在后。根据这样的规定,5个数字的所有的排列中,第一个排列是12345,最后一个是54321。这种判断排列顺序的方法称为字典序法

根据字典序法生成所有全排列的算法如下:
设P是集合{1、2、……n-1、n}的一个全排列:P = p1p2……pj-1pjpj+1……pn(1 ≤ p1、 p2、……、pn ≤ n-1)

  • 第一步:从排列的右端开始,找出第一个比右边数字小的数字的序号j,即j = max { i | pi < pi+1 ,i > j }
  • 第二步:在pj 的右边的数字中,找出所有比pj大的数字中最小的数字pk,即 k = min { i | pi > pj ,i > j}
  • 第三步:交换pi,pk
  • 第四步:再将排列右端的递减部分 [pj+1pj+2……pn]倒转,就得到了一个新的排列 P’ = p1p2…pj-1pkpnpn-1…pk+1pjpk-1…pj+1

例如839647521是数字1~9的一个排列。从它生成下一个排列的步骤如下:
(1) 自右至左找出排列中第一个比右边数字小的数字4 —— 839647521
(2) 在该数字后的数字中找出比4大的数中最小的一个5 —— 839647521
(3) 将5与4交换 —— 839657421
(4) 将7421倒转 —— 839651247
所以839647521的下一个排列是 839651247

为了得到集合{1、2、……n-1、n}的全体全全排列,可以从原始排列12……n开始,按照字典序法逐个生成它们的后继排列。当得到的后继排列是最后一个n……21时就结束。

3. 字典序算法代码:

输入:当前排序顺序int[] p
输出:每轮循环可得到下一个排列顺序,可以用列表存储所有结果

public static void dict(int[] p){
        int k = 0,j = 0;
        int n = p.length;
        // 每轮循环得到当前排列的下一个排列顺序,直至得到最后顺序
        while(true){
            // 计算下一次排列
            // 找出右端第一个小于右侧数字的数字p[j]
            for(j = n - 2;j >= 0;j--){
                if(p[j] < p[j + 1]){
                    break;
                }
            }
            if(j < 0){
                break;
            }
            // 找出右端大于p[j]的数字中最小的数字p[k]
            for(k = n - 1;k >= 0;k--){
                if(p[k] > p[j]){
                    break;
                }
            }
            // 交换p[j]和p[k]
            swap(p,j,k);
            k = n - 1;
            // 翻转右侧递减部分
            while(j < k){
                swap(p,++j,k--);
            }
            // 显示当前排列
            for(int i = 0;i < p.length;i++){
                System.out.print(p[i]);
            }
            System.out.println();
        }
    }
  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值