您的位置:首页 > 编程语言 > Python开发

全排列

2015-08-13 11:03 507 查看

1 全排列

1.1 含义

全排列,即给定一个集合,输出集合中元素所有组合的情况。

比如给定集合{1,2,3},应该输出:

123
132
213
231
312
321

1.2
递归的方式

思路1:

尝试给lst数组的第i个位置添加元素,如果i位置前没出现过该元素即可以添加该元素,如果出现过该元素,那么尝试另一个元素,如果成功了,那么递归的尝试给i+1位置添加元素,最终,如果所有位置的元素都被填满了,那么递归结束。

代码:

def permutation(lst, n, cur):
if cur == n: # 所有位置均被填满则输出
print lst
else:
for i in range(1, n+1): # 对当前位置尝试所有元素,看看能不能填入
flag = True # flag为标示,如果某元素可以填入,那么flag将保持True值不变
for j in range(0, cur): # 判断当前位置前的元素是否出现过想添加的元素i,如果出现过则不添加,否则可以添加
if lst[j] == i:
flag = False
break
if flag:
lst[cur] = i
permutation(lst, n, cur+1) # 递归的添加下一个位置

SIZE = 5
permutation([0]*SIZE, SIZE, 0)

注意:

上面的代码输出的是[1,2,3 ... n]的全排列,对于给定集合如['a', 'b', 'c', 'c']的全排列需要对代码进行修改或转换思路,比如,将上面字母的排列看成是它们位置的排列,而位置的排列可以直接使用上面的代码生成位置的排列,然后对应成对应的字符,当然,有时需要去重。

思路2:

对于给定序列的排列,也可以使用交换的方式进行排列。首先将i位置后面的所有元素依次与i位置的元素进行交换,再递归进行i+1位置与i+1后面所有的元素进行交换,如果这种交换进行到了序列的末尾,则进行输出返回,另外,在递归返回后需要重新将交换的元素再次互换,即恢复到未交换之前的状态,否则后面的交换将出错。

代码:

def permutation(lst, k, m):
if k == m-1: # 如果交换到了最后一个位置,那么输出序列,递归结束
print ''.join(lst)
else:
for i in range(k, m): # 依次将k位置后的元素与k位置的元素进行互换
lst[k],lst[i] = lst[i],lst[k]
permutation(lst, k+1, m) # 递归的进行下一个位置
lst[k],lst[i] = lst[i],lst[k] # 递归返回后需要恢复序列原先的位置,供下一轮互换

SIZE = 3
permutation(['a', 'c', 'b'], 0, SIZE)


1.3 非递归的方式

本非递归全排列算法为字典序排列算法,借鉴的是C++ STL中的next_permutation算法的思想。

其基本思想是:

对初始序列进行排列,找到所有排列中的最小的一个排列P0
找到比P0刚刚大一点的排列P1,P1比除P0以外的排列都小
循环执行第二步,直到找到一个最大的排列,算法结束
比如有序列‘12345’,它的最小的排列为‘12345’,比‘12345’稍微大一点的排列为‘12354’,接着是‘12435’ ... 一直到‘54321’。

算法思想:

对于给定的序列P = [A1, A2, A3 ... An] 首先对P按字典排序,得到P的最小的排列P0 = [A1, A2, A3 ... An],满足A1 < A2 < ... < An.

接着按如下步骤找到P的下一个排列。

从后向前,找到第一对为升序的相邻元素,即Ai < A(i+1),如果找不到则说明此时的序列为最大排列,已经找到了全部的全排列,可以退出了。
从后向前,找到第一个比Ai大的数Aj,交换Ai和Aj。
将Ai后面的数全部逆序倒置,由前面的步骤1和2知,交换后的Ai后面的数均为升序序列,这样逆序倒置后可以保证所得到的新的排列刚刚比上一个排列大。
重复步骤1-3,直到找到最大的排列。
比如当前的序列状态为:[4, 5, 3, 2, 1],第一步,我们发现找到的第一对升序的相邻元素为4和5,i = 0;第二步,我们发现比4大的元素为5,交换后得到序列[5, 4, 3, 2, 1];第三步,我们将i = 0后面的所有元素逆序倒置,得到[5, 1, 2, 3, 4],而该序列即为刚刚好比上一个序列[4, 5, 3, 2, 1]大的序列,输出;第四步,重复。

代码:

def reverse(lst, i, j):
'将lst中下标i到j之间的所有元素逆序倒置'
while i < j:
lst[i],lst[j] = lst[j],lst[i]
i += 1
j -= 1

def permutation(lst):
lens = len(lst)
if lens < 2:
return

while True:
print lst
i = lens - 2
while i >= 0:
if lst[i] < lst[i+1]: # 找到第一对相邻升序对
break
elif i == 0:
return
i -= 1

j = lens - 1
while j > i:
if lst[j] > lst[i]: # 找到找到第一个比lst[i]大的元素,下一步进行交换
break
j -= 1

lst[i],lst[j] = lst[j],lst[i]
reverse(lst, i+1, lens-1)

lst = [1,2,3,4,5]
permutation(lst)


1.4 permutations

事实上,python的itertools模块中提供了permutations函数,类似于C++的STL提供的库函数next_permutation,能够将给定的列表或者字符串进行全排列,返回的是一个迭代器。

利用该函数,我们可以轻易的进行给定列表的全排列,代码如下:

import itertools

s = 'abcc'
g = itertools.permutations(s)
# 升序并且需要去重,否则对于串“abcc”,会出现两次“abcc”,函数会将两个c当作不同的字母
g = sorted(set(g))
for i in g:
print ''.join(i)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python 全排列