【回溯算法】+ Leetcode相关题目

回溯思路主要根据代码随想录回溯篇
在这里插入图片描述
组合问题:N个数里面按一定规则找出k个数的集合
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
排列问题:N个数按一定规则全排列,有几种排列方式
棋盘问题:N皇后,解数独等等
在这里插入图片描述
代码模板:主要找到广度深度
可以从图中看出for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了。

res = []
path = []
def backtrack(input,path):
	if (终止条件) {
	    存放结果;
	    return;
	for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
    处理节点;
    backtracking(路径,选择列表); // 递归
    回溯,撤销处理结果
}
}

下面针对每道题进行详细的梳理

一、组合

组合

在这里插入图片描述
在这里插入图片描述

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        res = []
        path = []

        def backtrack(n, k, startindex, ):
            if len(path) == k:
                res.append(path[:])
                return

            for i in range(startindex, n - (k - len(path)) + 1 + 1):  # 减枝操作
                path.append(i)
                backtrack(n, k, i + 1)
                path.pop()

        backtrack(n, k, 1)
        return res

组合总和III

在这里插入图片描述
在这里插入图片描述

class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        res = []
        path = []

        def backtrack(k, targetSum, startInedx):
            if sum(path[:]) > targetSum:
                return
            if len(path) == k and sum(path[:]) == targetSum:
                res.append(path[:])
                return

            for i in range(startInedx, 10):
                path.append(i)
                backtrack(k, targetSum, i + 1)
                # k += 1
                # n += i
                path.pop()

        backtrack(k, n, startInedx=1)
        return res

电话号码的字母组合

在这里插入图片描述
在这里插入图片描述

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        const = {
            '0': '',
            '1': '',
            '2': 'abc',
            '3': 'def',
            '4': 'ghi',
            '5': 'jkl',
            '6': 'mno',
            '7': 'pqrs',
            '8': 'tuv',
            '9': 'wxyz'
        }

        res = []
        if not digits:
            return res
        def backtrack(digits, index, s):
            # 长度即为取的index+1
            if index == len(digits):
                res.append(s)
                return
            letter = const[digits[index]]
            index += 1
            for i in letter:
                s += i
                backtrack(digits, index, s)
                s = s[:-1]
        backtrack(digits, 0, s = '')
        return res

组合总和

在这里插入图片描述
在这里插入图片描述

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        res = []

        def backtrack(path, candidates):
            if sum(path[:]) > target:
                return
            if sum(path[:]) == target:
                res.append(path[:])
                return
            for i in range(len(candidates)):
                path.append(candidates[i])
                backtrack(path, candidates[i:])
                path.pop()

        backtrack(path=[], candidates=candidates)
        return res

组合总和II

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        res = []
        candidates = sorted(candidates)
        def backtrack(path, candidates):
            if sum(path[:]) > target:
                return
            if sum(path[:]) == target:
                res.append(path[:])
                return
            for i in range(len(candidates)):
                if i>0 and candidates[i-1]==candidates[i]:
                    continue
                path.append(candidates[i])
                backtrack(path, candidates[i+1:])
                path.pop()

        backtrack(path=[], candidates=candidates)
        return res

二、分割

分割回文串

在这里插入图片描述

在这里插入图片描述

class Solution:
    def huiwen(self, sub_s):
        """验证回文串"""
        if sub_s:
            for sub in sub_s:
                if sub == sub[::-1]:
                    continue
                else:
                    return False
            return True
        else:
            return False

    def partition(self, s: str) -> List[List[str]]:
        res = []
        init_len = len(s)

        def backtrack(s, list_sub_s):
            tmp_len = 0
            for idx in list_sub_s:
                tmp_len += len(idx)
            if tmp_len == init_len:
                res.append(list_sub_s[:])
                return
            for i in range(len(s)+1):
                sub = s[:i]
                if sub==sub[::-1] and sub!='':
                    list_sub_s.append(s[:i])
                    backtrack(s[i:], list_sub_s)
                    list_sub_s.pop()
        backtrack(s, [])

复原IP地址

在这里插入图片描述
在这里插入图片描述

class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        res = []

        def backtrack(path, s):
            if 4 - len(path.split('.')) > len(s):
                return
            if path!='':
                for i in path.split('.'):
                    if int(i)>255:
                        return
            if len(path.split('.')) == 4 and s == '':
                res.append(path)
                return
            for i in range(len(s)):
                if path != '':
                    path += '.'
                if len(s[:i+1]) == len(str(int(s[:i + 1]))):
                    path += str(int(s[:i + 1]))
                    backtrack(path, s[i + 1:])
                    path = path[:-len(str(int(s[:i+1])))]
                    path = path[:-1]
        backtrack('', s)
        return res

三、子集

子集II

在这里插入图片描述
在这里插入图片描述

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        nums = sorted(nums)
        res = []
        def backtrack(nums,path):
            res.append(path[:])

            for i in range(len(nums)):
                if i>0 and nums[i-1]==nums[i]:
                    continue
                path.append(nums[i])
                backtrack(nums[i+1:],path)
                path.pop()
        backtrack(nums,[])
        return res

递增子序列

在这里插入图片描述
在这里插入图片描述

class Solution:
    def findSubsequences(self, nums: List[int]) -> List[List[int]]:
        res = []
        def backtrack(nums,path):
            if len(path)>1:
                res.append(path[:])
                # return  # 这里不return,因为还要继续往下寻找,知道找到叶子节点,自动停止
            uset = set()  # 记录本层用过的数字,后面不能再用了
            for i in range(len(nums)):
                if (path and path[-1] > nums[i]) or nums[i] in uset:
                    continue
                uset.add(nums[i])
                path.append(nums[i])
                backtrack(nums[i+1:],path)
                path.pop()
        backtrack(nums,[])
        return res

四、排列

全排列

在这里插入图片描述在这里插入图片描述

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        res = []
        lens = len(nums)
        def backtrack(nums,path):
            if len(path) == lens:
                res.append(path[:])
                return
            for i in range(len(nums)):
                path.append(nums[i])
                backtrack(nums[:i]+nums[i+1:],path)
                path.pop()
        backtrack(nums,[])
        return res

全排列 II

在这里插入图片描述
在这里插入图片描述

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        res = []
        lens = len(nums)

        def backtrack(nums, path):
            if len(path) == lens:
                res.append(path[:])
                return
            uset = set()
            for i in range(len(nums)):
                if nums[i] in uset:
                    continue
                uset.add(nums[i])
                path.append(nums[i])
                backtrack(nums[:i] + nums[i + 1:], path)
                path.pop()

        backtrack(nums, [])
        return res

五、棋盘问题

N皇后

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
递归深度就是row控制棋盘的行每一层里for循环的col控制棋盘的列,一行一列,确定了放置皇后的位置。
每次都是要从新的一行的起始位置开始搜,所以都是从0开始。

按照如下标准去重:

  • 不能同行
  • 不能同列
  • 不能同斜线 (45度和135度角)
class Solution(object):
    def isValid(self,row,col,chessboard):  # 验证棋盘是否合法  
        # 判断45度
        i,j = row-1,col-1
        while i>=0 and j>=0:
            if chessboard[i][j] == 'Q':
                return False
            i-=1
            j-=1
        # # 判断行
        for i in range(col):
            if chessboard[row][i] == 'Q':
                return False
        # 判断列
        for i in range(row):
            if chessboard[i][col] == 'Q':
                return False
        # 判断135度
        i, j = row - 1, col + 1
        while i >= 0 and j < len(chessboard):
            if chessboard[i][j] == 'Q':
                return False  # 右上方向已经存在皇后,不合法
            i -= 1
            j += 1
        return True

    def solveNQueens(self, n):
        """
        :type n: int
        :rtype: List[List[str]]
        """
        res = []
        chessboard = ['.'* n for _ in range(n)]
        def backtrack(row,chessboard):  # 从0行开始
            if row == n:
                res.append(chessboard[:])
                return
            for col in range(n):  # 宽度搜寻列
                if self.isValid(row,col,chessboard):
                    chessboard[row] = chessboard[row][:col]+'Q'+chessboard[row][col+1:]
                    backtrack(row+1,chessboard)
                    chessboard[row] =chessboard[row][:col]+'.'+chessboard[row][col+1:]
        backtrack(0,chessboard)
        return res

解数独

在这里插入图片描述在这里插入图片描述

六、其他

重新安排行程

在这里插入图片描述
这道题目有几个难点:

一个行程中,如果航班处理不好容易变成一个圈,成为死循环
有多种解法,字母序靠前排在前面,让很多同学望而退步,如何该记录映射关系呢 ?
使用回溯法(也可以说深搜) 的话,那么终止条件是什么呢?
搜索的过程中,如何遍历一个机场所对应的所有机场。

本题以输入:[[“JFK”, “KUL”], [“JFK”, “NRT”], [“NRT”, “JFK”]为例,抽象为树形结构如下:

在这里插入图片描述

class Solution(object):
    def findItinerary(self, tickets):
        """
        :type tickets: List[List[str]]
        :rtype: List[str]
        """
        tickets = sorted(tickets)
        res = []
        path = ['JFK']
        lens = len(tickets)
        used = [0]*lens    # 用于存在是否使用过的状态

        def backtrack(tickets,path,cur):
            if len(path) == lens+1:
                res.append(path[:])
                return True
            # 补充返回的情况
            for i,ticket in enumerate(tickets):
                if ticket[0] == cur and used[i]==0:   # 如果当前机票的起点等于上一个终点,且该机票未使用
                    used[i] = 1  # 标记为已使用
                    path.append(ticket[1])
                    stack = backtrack(tickets,path,ticket[1])
                    path.pop()
                    used[i] = 0  # 还原为未使用
                    if stack:   # 如果找到了一条路径,就不继续搜索了。 否则会超时。
                        return True
                    
        backtrack(tickets,path,"JFK")
        return res[0]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值