本期任务:介绍算法中关于回溯思想的几个经典问题
一、问题描述
问题来源:LeetCode 46. 全排列
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
二、算法思路
- 使用回溯思想,暴力穷举,每一个位置都可能有1-3共3种可能,所有可能的摆放方式共有 3 3 3^3 33,穷举过程遵循深度优先搜索规则。
- 剪枝策略:维护一个existed数组,用于记录每个数字的出现与否,再次出现则跳过。
- 结算情形:所有数字都已遍历
三、Python代码实现
class AllPermutation():
def __init__(self, array):
self.arr = array
self.size = len(self.arr)
self.res = [-1] * self.size # 记录当前排列
self.ans = []
self.visited = [0] * self.size # 记录每个数字的被访问情况
def all_permutation(self, index=0):
"""回溯的主逻辑"""
if index == self.size: # 找到一个排列
self.ans.append(list(self.res))
return
for i, v in enumerate(self.arr): # 每一个给位置都有三种可能
if not self.visited[i]: # 当前位置未被访问过
self.res[index] = v
self.visited[i] = 1
self.all_permutation(index + 1)
self.visited[i] = 0 # 由于回溯过程对三个二维数组进行了修改,故回溯完成需要对称复原。
def main():
ap = AllPermutation([1, 2, 3])
ap.all_permutation()
print(ap.ans)
if __name__ == '__main__':
main()
运行结果
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
四、问题变形
问题来源:LeetCode 47. 全排列II
- 题目描述
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
- 解决方案:
基于上文代码,仅需修改一行即可(即在计算的时候,判断当前排列是否已经出现过。):
# if index == self.size: # 找到一个排列(旧)
if index == self.size and self.res not in self.ans: # 找到一个排列(新)
self.ans.append(list(self.res))
return