• 非递归全排列 python实现


    python algorithm

    全排列(Permutation)


    排列Permutation)是将相异物件或符号根据确定的顺序重排。每个顺序都称作一个排列。
    例如,从一到六的数字有720种排列,对应于由这些数字组成的所有不重复亦不阙漏的序列,例如4, 5, 6, 1, 2, 3 与1, 3, 5, 2, 4, 6。 -- From Wikipedia

    (n)个相异元素中取出 (k)个元素,(k)个元素的排列数量为:

    [ {P_{k}^{n}={frac {n!}{(n-k)!}}} ]

    其中(P)意为Permutation(排列),(!)表示阶乘运算。全排列而取(k)(n),则结果为(n!)

    全排列生成算法

    1. 字典序法

      字典序,就是将元素按照字典的顺序(a-z, 1-9)实际上是ASCII编码值进行排列。以字典的顺序作为比较的依据,可以比较出两个串的大小。
      比如 "1" < "13"<"14"<"153", 就是按每个数字位逐个比较的结果。对于一个串(“123456789”), 可以知道最小的串是(“123456789”),而最大的串(“987654321”)。这样针对这个串以字典序法生成全排列生成全排列,就是依次生成

      [“123456789”->“123456798”->......->"987654312"->"987654321" ]

      这样的串。字典序法要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。

    2. 邻位对换法

      该算法由Johnson-Trotter首先提出,是一个能快速生成全排列的算法。它的下一个全排列总是上一个全排列对换某相邻两位得到的。如果已知n-1个元素的排列,将n插入到排列的不同位置,就得到了n个元素的排列。用这种方法可以产生出任意n个元素的排列。这个方法有一个缺点:为了产生n个元素的排列,我们必须知道并存储所有n-1个元素的排列,然后才能产生出所有n阶排列。

    3. 递增进位制法

      这个算法是基于序列的递增进位制数[3]。递增进位制数是指数字的进制随着位数的递增而递增。一般情况下,数字最右边的进制是2,次右边的进制是3,以此类推。n位递增进位制数一共包含n!个数字,所以它可以与全排列生成算法结合在一起。

    4. 递减进位制法

      该方法与递增进位制法的原理相似,不同的是它定义的“递减进位制数”是数字的进制随着位数的递增而递减。这种进制一般最左边的进制是2,次左边的进制是3。其余原理与递增进位制法基本相同。


    Python实现

    字典序法
    非递归算法

    (P)是集合({1,2,……n-1,n})的一个全排列:
    (P=P_1P_2……P_{j-1}P_jP_{j+1}…P_n(1≤P_1,P_2,……,P_n≤n))

    1. 从排列的右端开始,找出第一个比右边数字小的数字的序号j,即

      [j=max{ j|Pj<Pj+1, 1 < j < n} ]

      (P_j)的右边的数字中,找出所有比(P_j)大的数字中最小的数字(P_k),即

      [P_k=min{Pi|P_i>P_j,i>j} ]

    2. 交换(P_j)(P_k)

    3. 再将排列右端的递减部分(P_{j+1}P_{j+2}…P_n) 倒序可以得到一个新的排列

      (P'=P_1P_2…P_{j-1}P_kP_n…P_j..P_{j+2}P_{j+1})

    代码
    #!/usr/bin/env python2
    # -*- coding: utf-8 -*-
    """
    @author: gsharp
    """
    def Swap(n,a,b):
        n[a],n[b] = n[b],n[a]
        return None
    # 从n[begin]开始反转数组 xxx543->xxx345
    def Reverse(n,begin):
        if len(n) > begin:
            i = begin
            j = len(n)-1
            while i < j:
                Swap(n,i,j)
                i += 1
                j -= 1
        return n
    #  查找n[i-1:size]中比n[i]大的最小数
    def FindMin(n,i):
        j = len(n)-1
        k = i + 1
        while j > i:
            if n[j] > n[i] and n[j] < n[k]:
                k = j
            j -= 1
        return k
    
    def Permut(n):
        count = 0
        j = len(n) -1  
        if j < 1:
            return n
        else :
            print n
            count += 1
            while j >= 1:
                i = j - 1
                if n[i] < n [j] : # 逆序找到第一个小于右侧数的位置i
                    k = FindMin(n,i)
                    Swap (n,i,k)
                    Reverse (n,j)
                    j = len(n) - 1
                    count += 1
                    print n
                else :
                    j -= 1
        print count
    
    n =[1,2,3,4,5,6]
    Permut(n)
    

    注意:

    1. 这里只能对于具有可比较值的列表排序,对于如【'~','!','@','#'】无法直接排序。
    2. 初始序列必须为最小序列,否则无法列出全部排列。可先使用快速排序来排序后作为输入。
  • 相关阅读:
    xna 添加一个精灵1
    【leetcode】705. 设计哈希集合
    【leetcode】1603. 设计停车系统
    【leetcode】设计有序流
    【leetcode】旅行终点站
    【leetcode】检测大写字母
    【leetcode】重复至少 K 次且长度为 M 的模式
    【leetcode】二叉树的直径
    【leetcode】公交站间的距离
    【leetcode】分糖果
  • 原文地址:https://www.cnblogs.com/sonnet/p/8996941.html
Copyright © 2020-2023  润新知