回溯算法-全排列

回溯的核心框架

#参考fucking-algorithm
def backtrace(路径, 选择列表):
    if 满足递归终止条件:
        保存结果
        返回
    for 选择 in 选择列表:
        #做选择
        将当前选择从选择列表中移除
        路径.append(选择)
        #递归
        backtrace(路径, 选择列表)
        #撤销选择
        路径.pop()
        将该选择再次加入选择列表

做回溯的题,基本就这么一个框架,比如这次的全排列问题。
leetcode46 没有重复元素的全排列
回溯算法-全排列_第1张图片

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        n = len(nums)
        status = [False] * n
        res = []
        def backtrace(nums, status, tmp, n):
        	#终止条件
            if len(tmp) == n:
                res.append(tmp[:])
                return
            #选择列表
            for i in range(n):
            	#判断当前选择是否已存在与路径中,因为每次都是从0开始循环选择
            	#避免重复选择同一个元素
                if not status[i]:
                	#做选择,吧当前选择从候选列表移除,这里用一个状态数组
                	#来记录数组中元素的使用情况,赋值为True时,即表示从候选列表移除
                    status[i] = True
                    tmp.append(nums[i])
                    backtrace(nums, status, tmp, n)
                    #撤销选择
                    tmp.pop()
                    status[i] = False
        backtrace(nums, status, [], n)
        return res

leetcode 47 有重复元素的全排列
回溯算法-全排列_第2张图片
有重复元素的时候,我们需要先对数组进行排序,方便后面使用回溯算法。经过排序之后,重复元素一定是相邻的,当遍历到元素i,如果nums[i] == nums[i-1],并且nums[i-1]的状态还是未使用过的,那么说明我们已经遍历过从nums[i-1]开头的全排列情况了,这时候就不用再去回溯nums[i]的情况,也就是题目要求的不重复的全排列

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        if not nums:
            return []
        nums.sort()
        n = len(nums)
        status = [False] * n
        res = []
        def backtrace(nums, status, tmp):
        	#同样的终止条件
            if len(tmp) == n:
                res.append(tmp[:])
                return
            for i in range(n):
                if not status[i]:
                #对重复元素的处理,相当于一个剪枝操作
                    if i > 0 and nums[i-1] == nums[i] and not status[i-1]:
                        continue
                    #做选择
                    status[i] = True
                    tmp.append(nums[i])
                    backtrace(nums, status, tmp)
                    #撤销选择
                    tmp.pop()
                    status[i] = False
        backtrace(nums, status, [])
        return res

这两道题对于回溯的框架体现得很明显,除了对一些情况得处理略微不同之外,其他的完全一样。我们要做的就是在那个框架上,根据题目要求,做出相应的处理,也就是在做选择前,我们要确定把什么样的选择添加到当前的路径里。回溯的特点就是“有借有还”,我们先借一下,用了之后又还回去。

你可能感兴趣的:(回溯算法-全排列)