全排列的非递归实现

以前写过全排列的递归算法,思路和实现都比较简单。上周练习python,遇到了全排列的问题,就想着用非递归实现实现以下。没想到从中午想到了半夜,草稿用了好几张,也没想到好的idea。第二天早上在地铁上,在手机拨号页面演示了一会,竟然有了思路。

假定:从小打大排序,称为正序,反之称为逆序

我的思路是:以数字序列 312为例,排序后123,排列有 [123,132,213,231,312,321],第一项和最后一项分别是正序和逆序,每一项的值递增的。
如果设计一套流程,保证从正序到逆序,按值递增的原则,生成每一项,就完成了这个序列的全排列

那么怎么实现这个递增过程呢,经过一系列的手算,我发现了一些规律:

  1. 判断是否逆序,如果不是逆序进行下一步,否则,完成排列。
  2. 从右往左寻找第一个数A,使得A小于它右边相邻的数B
  3. 从B到往右到末尾寻找C, 使得C是最小的大于A的数。因为A右边的序列(不含A)是有序的,所以只需找到比A小的数的左边的数。如果都大于A,则取最右边一位
  4. 交换A和C
  5. B到末尾按正序排序

举几个例子:
例子1:
09442,
1. 一眼看出,不是逆序
2. 从右往左查找第一个小于右邻的数,找到0
3. 然后从9往右找最小的大于0的数为2
4. 0和2交换得:29440
5. 9到末尾正序排序得:20449
从09442到20449,有值位于这两个数之间的排列吗?没有的话,这个就是我们寻找的值。

例子2:
38521
1. 非逆序
2. 找到3<8
3. 找到5>3>2,之前说了,3后面是有序的,2<3,后面就不用找了,最小的大于3的就是5
4. 3和5换位得:58321
8到末尾正序重排得:51238

例子3:
12
1. 非逆序
2. 1
3. 2
4. 1、2交换得21
5. 2到末尾交换得21

python新手,现学现用,下面是python3实现:

def reverse_at(sstr, i, j):
    return sstr[:i] + sstr[i:j + 1][::-1]


def next_permu(sstr):
    slen = len(sstr)
    tmp = -1
    tmp2 = -1
    for i in range(0, slen - 1)[::-1]:
        if sstr[i] < sstr[i + 1]:
            tmp = i
            break
    if tmp != -1:
        for j in range(tmp + 1, len(sstr) - 1):
            if sstr[j] > sstr[tmp] and sstr[j + 1] <= sstr[tmp]:
                tmp2 = j
        if tmp2 == -1:
            tmp2 = slen - 1
        sstr = swap(sstr, tmp, tmp2)
        sstr = reverse_at(sstr, tmp + 1, slen - 1)
        return sstr
    return None


def swap(str, i, j):
    str=list(str)
    ch = str[i]
    str[i] = str[j]
    str[j] = ch
    return str


def permutations(sstr):
    print(sstr)
    lst=sstr
    while not is_reversed(sstr):
        sstr = next_permu(sstr)
        lst+= ' ' + ''.join(sstr)
    return lst.split()


def is_reversed(sstr):
    for i in range(0, len(sstr) - 1):
        if sstr[i] < sstr[i + 1]:
            return False
    return True

print(permutations(''.join(sorted('bcad'))))
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值