全排列,即给定一个集合,输出集合中元素所有组合的情况。
比如给定集合{1,2,3},应该输出:
123321
思路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)
本非递归全排列算法为字典序排列算法,借鉴的是C++ STL中的next_permutation算法的思想。
其基本思想是:
比如有序列‘12345’,它的最小的排列为‘12345’,比‘12345’稍微大一点的排列为‘12354’,接着是‘12435’ ... 一直到‘54321’。
算法思想:
对于给定的序列P = [A1, A2, A3 ... An] 首先对P按字典排序,得到P的最小的排列P0 = [A1, A2, A3 ... An],满足A1 < A2 < ... < An.
接着按如下步骤找到P的下一个排列。
比如当前的序列状态为:[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)
事实上,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)