一:leetcode 46. Permutations
1:题意
给定一个数组,数组内所有元素是唯一的,返回这些数的排列组合!
2:输入输出
输入:[1,2,3]
输出:[ [1,2,3], [1,3,2],[2,1,3], [2,3,1],[3,1,2],[3,2,1]]
3:分析
如样例所示:输入[1,2,3]时,构造的状态树如下所示:
回溯法:当使用1,余下只能使用2,3排列,选择2只能选择3,得到一种全排列1,2,3. 此时状态回退,选择3只能选择2,得到一种全排列1,3,2。
从上图来看,回溯的意思就是,当深度优先搜索到叶子节点时,此时保存这一条路径就是一个排列,然后沿着叶子节点向根节点回溯,再沿着没有访问过的节点深度优先搜索到其他叶子节点,又是另外一个排列了
4:python代码实现
回溯+hash来实现:
if not nums: return [[]]
self.res = []
vis = set() # 避免重复访问
def traceback(path, nums, vis):
if len(path) == len(nums): # 到达叶子节点了
self.res.append(path[:])
return # 不用写else了
for n in nums: # 把数组中的元素都遍历一次。每个元素都当一次根节点
if n in vis: continue
path.append(n)
vis.add(n)
traceback(path, nums, vis) # 一直走到叶子节点
path.pop() # 把一条路径的最后节点去掉,往根节点回溯
vis.remove(n)
traceback([], nums, vis)
return self.res
插值法实现
def permute(self, nums):
# 方法一:插入法,16ms,11.9MB 还不错
res = [[]]
if not nums: return res
for num in nums: # 单数表示一个数,复数表示数组
perm = [] # 存中间结果,如 [[1,2],[2,1]]
for line in res: # 取出一行出来
L = len(line)+1 # 加1是因为空数组的时候需要初始化
for i in xrange(L):
perm.append(line[:i] + [num] + line[i:]) # insert num
# print(perm)
res = perm # 指向同一个
return res
二:leetcode 47. Permutations II
1:题意
给定一个数组,数组内所有元素有重复,返回这些数的排列组合,答案没有重复值!
2:输入输出
输入:[1,1,2]
输出:[ [1,1,2], [1,2,1], [2,1,1] ]
3:分析
如样例所示:输入[1,1,2]时,遍历的状态如下图所示:
如上图所示:输入[1,1,2],
当访问第一个值1时,接下来可以访问第二个值1,最后访问第三个值2。
当访问第二个值1时,此时1已经当过根节点了,所以要及时剪枝,直接返回
当访问第三个值2时,依次访问1,1是可以的,但是再次访问1时也要及时剪枝,已经访问过了,不再重复访问。
4:python代码实现
回溯法来实现,并通过vis数组来记录每一个值是否访问过:
if not nums: return [[]]
self.res = []
nums.sort() # 保证相同数字会相邻
vis = [False] * len(nums)
def traceback(line, vis, nums):
if len(line) == len(nums): # 到了叶子节点了
self.res.append(line[:]) # 得用切片来复制数组
return # 不用写else了
for i in range(len(nums)):
if vis[i]: continue # nums[i]已经访问过了,剪枝(上图中第一个红色叉)
# nums[i]没访问过,并且nums[i]与nums[i-1]相同也需要剪枝(上图中第二个红色叉)
if i>0 and nums[i] == nums[i-1] and not vis[i-1]: continue
line.append(nums[i])
vis[i] = True
traceback(line, vis, nums)
line.pop() # 去掉一条路径中的最后一个来回溯到前一个状态
vis[i] = False
traceback([], vis, nums)
return self.res
三:字符串的排列(剑指offer)
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
此外,输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
# -*- coding:utf-8 -*-
class Solution:
def Permutation(self, ss):
# write code here
# 回溯法
if not ss: return []
self.res = []
string = list(ss) # [s for s in ss] str->array
string.sort() # 字符排序,让重复字符都在一起
vis = [False] * len(string)
def traceback(path, string, vis):
if len(path) == len(string):
self.res.append(''.join(path))
return
for i in range(len(string)):
if vis[i]: continue
if i > 0 and string[i] == string[i-1] and vis[i-1] == False: continue # 条件都不能少,不然通不过
path.append(string[i])
vis[i] = True
traceback(path, string, vis)
path.pop()
vis[i] = False
traceback([], string, vis)
return self.res
31. Next Permutation
在一个排列按照字母表顺序的情况下,找到下一个更大的排列是多少。如果已经是最大的,那么应该翻转。必须原地翻转。
class Solution(object):
def nextPermutation(self, nums):
n = len(nums) - 1
i = n
while i > 0 and nums[i] <= nums[i-1]: # 逆序遍历,递增序列
i -= 1
# i-1才是我所需要的数
if i > 0:
# 逆序找第一个比nums[i-1]大的数
j = n
while j >= 0 and nums[j] <= nums[i-1]:
j -= 1
nums[i-1], nums[j] = nums[j], nums[i-1] # 交换
# 从i到结尾都得逆序
self.reverse(nums, i)
def reverse(self, nums, start):
i, j = start, len(nums)-1
while i < j:
nums[i], nums[j] = nums[j], nums[i]
i += 1
j -= 1