回溯函数的组成
1.回溯出口,当找到了一个问题的解时,存储该解。
2.回溯主体,就是遍历当前的状态的所有子节点,并判断下一个状态是否是满足问题条件的,如果满足问题条件,那么进入下一个状态。
3.状态返回,如果当前状态不满足条件,那么返回到前一个状态。
1 问题描述
给定一个不包含重复数字的序列,返回所有不重复的全排列。
1.1 问题分析
遍历所有的解空间树即可找到答案。
首先定义一个回溯函数
# combination 为当前的状态
backtrack(combination=[])
那么它的出口部分也很好写,就是当combination的长度等于序列的长度时,就找到了问题的一个解。
if len(combination) == len(nums):
answer.append(combination)
然后是回溯函数的主体部分,我们要遍历当前状态下的所有子节点,并判断子节点是否还符合问题的条件,那么对于这个问题,因为全排列的数是不能够重复的,所以我们的判断方式是当前的数没有包含在combination中,那么进入下一个状态。
for num in nums:
if num not in combination:
backtrack(combination+[num])
那么这个问题需要返回上一个状态吗?答案是不需要,因为backtrack的下一个状态的写法是backtrack(combination + [num]),这并不会改变我们当前的combination的值,因为我们没有对combination对象进行一个重新的赋值操作。
如果说修改一下回溯函数的主体。
for num in nums:
if num not in combination:
combination.append(num)
backtrack(combination+[num])
那么这时候,combination的值被改变了,所以需要写一个返回上一个状态的代码。
for num in nums:
if num not in combination:
combination.append(num)
backtrack(combination)
combination.pop()
并且,因为我们传入的是相当于是combination对象,所以在存储解的时候需要深拷贝。
if combination.__len__() == nums.__len__():
solution = copy.deepcopy(combination)
answer.append(solution)
3.1.2 完整代码
import copy
class Solution:
def permute(self, nums: list):
answer = []
def backtrack(combination=[]):
if combination.__len__() == nums.__len__():
solution = copy.deepcopy(combination)
answer.append(solution)
return
for num in nums:
if num not in combination:
combination.append(num)
backtrack(combination)
combination.pop()
backtrack()
return answer
几大习题:
(1)装载问题
(2)0-1背包问题
(3)旅行售货员问题
(4)八皇后问题
(5)迷宫问题
(6)图的m着色问题