【LeetCode】DFS+回溯

目录

注意:

17. 电话号码的字母组合

79. 单词搜索

46. 全排列

47. 全排列 II

78. 子集

90. 子集 II

216. 组合总和 III

52. N皇后 II

473. 火柴拼正方形


注意:

继续和b站宝藏up主大雪菜一起刷题

https://www.bilibili.com/video/BV1M4411Q7td

参考刷题视频

17. 电话号码的字母组合

思路:

  • 主要是需要枚举所有的字母组合,可以利用循环
class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        if not digits:
            return []
        ch = ["abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"]
        res = [""]
        for i in range(len(digits)):
            now = digits[i]
            tmp = []
            for c in ch[ord(now)-ord('2')]:
                for j in res:
                    tmp.append(j+c)
            res = tmp
        return res

79. 单词搜索

思路:

  • 由于没有规定起点,所以得依次遍历,看和word是否匹配
  • dfs中匹配相同的地方,修改成'.',回溯的时候再还原
class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        if len(board)==0 or len(board[0])==0:
            return False
        for i in range(len(board)):
            for j in range(len(board[0])):
                if self.dfs(board, word, i, j, 0):
                    return True
        return False
    
    def dfs(self, board, word, x, y, u):
        move = [(0,1),(1,0),(0,-1),(-1,0)]
        # 必须放在u==len(word)-1之前
        if board[x][y]!=word[u]:
            return False
        if u == len(word)-1:
            return True
        board[x][y] = '.'
        for i in range(4):
            newx = x + move[i][0]
            newy = y + move[i][1]
            if 0<=newx<len(board) and 0<=newy<len(board[0]):
                if self.dfs(board, word, newx, newy, u+1):
                    return True
        board[x][y] = word[u]
        return False

46. 全排列

思路:

  • 利用dfs+回溯,枚举每个位置放哪个数,最后长度达到了len(nums)就直接将当前path加入res
class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        visited = [False]*len(nums)
        ans = []
        self.dfs(nums, 0, [], ans, visited)
        return ans
    
    def dfs(self, nums, u, path, ans, visited):
        if u == len(nums):
            ans.append(path[:])
            return
        for j in range(len(nums)):
            if not visited[j]:
                visited[j] = True
                path.append(nums[j])
                self.dfs(nums, u+1, path, ans, visited)
                path.pop()
                visited[j] = False

47. 全排列 II

思路:

  • 这个题由于要考虑重复的数字,所以先用排序的方法,让一样的数字待在一起,然后一样的数字全排列的起点就不能从头开始,而应该从上一个一样的数字后面开始
  • 与上一题相比,这一题是枚举每个数放哪个位置
class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        nums = sorted(nums)
        ans = []
        path = [-1]*len(nums)
        visited = [False]*len(nums)
        self.dfs(nums, 0, path, ans, 0, visited)
        return ans
    
    def dfs(self, nums, u, path, ans, start, visited):
        if u == len(nums):
            ans.append(path[:])
            return
        for i in range(start, len(nums)):
            if not visited[i]:
                visited[i] = True
                path[i] = nums[u]
                if u+1<len(nums) and nums[u+1]!=nums[u]:
                    start = 0
                else:
                    start = i+1
                self.dfs(nums, u+1, path, ans, start, visited)
                visited[i] = False

78. 子集

思路:

  • 递归的方法可以解决,同时也可以采用另一种二进制的方法
  • 以[1,2,3]为例,有三个数可以表示为000~111,每一位为1代表这个数有,就刚好得到答案
  • i>>j&1,意思是i右移j位再和1相与
class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = []
        for i in range(2**len(nums)):
            tmp = []
            for j in range(len(nums)):
                # print(i<<j)
                if (i>>j)&1:
                    tmp.append(nums[j])
            res.append(tmp)
        return res

90. 子集 II

思路:

  • 因为只是需要得到集合,不用考虑顺序。
  • 考虑每个数字出现的次数,用k来统计,每一次dfs得到出现0次、1次....k次的数字
  • 每次统计完需要回溯
class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        res = []
        nums = sorted(nums)
        self.dfs(nums, 0, res, [])
        return res
    
    def dfs(self, nums, u, res, path):
        if u == len(nums):
            res.append(path[:])
            return 
        # 统计有几个nums[u]
        k = 0
        while k+u < len(nums) and nums[k+u]==nums[u]:
            k += 1
        for i in range(k+1):
            self.dfs(nums, u+k, res, path)
            path.append(nums[u])
        # 恢复现场
        for i in range(k+1):
            path.pop()

216. 组合总和 III

思路:

  • 利用dfs枚举每一个组合,答案为n就加进ans里
class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        res = []
        self.dfs(k, 1, n, res, [])
        return res
    
    def dfs(self, k, start, n, res, path):
        if not k:
            if not n:
                res.append(path[:])
                return
        for i in range(start, 10):
            path.append(i)
            self.dfs(k-1, i+1, n-i, res, path)
            path.pop()
                

52. N皇后 II

思路:

  • 首先这个nXn的棋盘上,棋子不能在同一行,不能在同一列,不能在同对角线上
  • 对角线可以用x+y,x-y来表示,其中x即第几行,y为第几列,考虑x-y可能为负数,加上一个n即可
  • dfs查看每一行哪个地方满足条件,接着dfs下一行,不满足返回探查下一个位置
class Solution:
    def totalNQueens(self, n: int) -> int:
        res = [0]
        col = [False]*(n)
        d = [False]*(2*n)
        ud = [False]*(2*n)
        self.dfs(0, res, n, col, d, ud)
        return res[0]
    
    def dfs(self, u, res, n, col, d, ud):
        if u == n:
            print(res)
            res[0] += 1
            return
        for i in range(n):
            if (not col[i]) and (not d[u+i]) and (not ud[u-i+n]):
                # print("{} {}".format(i, n))
                col[i] = d[u+i] = ud[u-i+n] = True
                self.dfs(u+1, res, n, col, d, ud)
                col[i] = d[u+i] = ud[u-i+n] = False

473. 火柴拼正方形

思路:

  • 这题主要是剪枝的问题
  • 从大到小枚举每条边
  • 每条边内部的木棒长度规定成从大到小
  • 如果当前木棒拼接失败,则跳过接下来所有长度相同的木棒
  • 如果当前木棒拼接失败,且是当前边的第一个,则直接剪掉当前分支
  • 如果当前木棒拼接失败,且是当前边的最后一个,则直接剪掉当前分支
class Solution:
    def makesquare(self, nums: List[int]) -> bool:
        # 查看过与否的标记
        visited = [False]*len(nums)
        # 列表之和
        sum_ = sum(nums)
        if (sum_ == 0) or (sum_%4 != 0):
            return False
        # 排序 使一样的数字挤在一起
        nums = sorted(nums, reverse = True)
        return self.dfs(nums, visited, 0, 0, sum_//4)
    
    def dfs(self, nums, visited, u, cur, length):
        # 得到一个满足条件的边
        if cur == length:
            u += 1
            cur = 0
        # 四条边都得到了
        if u == 4:
            return True
        i = 0
        while i < len(nums):
            if not visited[i] and nums[i]<=length-cur:
                visited[i] = True
                if self.dfs(nums, visited, u, cur+nums[i], length):
                    return True
                visited[i] = False
                # 剪枝4
                if not cur:
                    return False
                # 剪枝5
                if cur+nums[i] == length:
                    return False
                # 剪枝3
                if i+1<len(nums) and nums[i+1]==nums[i]:
                    i += 1
            i += 1
        return False

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值