leetcode-dfs-v2

 dfs的写法包括回溯和普通的深度优先搜索。

两者本质上是一样的,排列组合子集问题本文的回溯法,先append当前路径,然后再pop,和把当前路径传入到函数参数里本质上是一样的,就是写法不一样而已。参见括号生成的两种写法。

组合总和的添加结果是在for循环中,因为和target有关系,子集和全排列添加结果是在for循环外边。

排列类dfs和组合类dfs的区别

LintCode-15: Permutations (排列类DFS经典题!)_roufoo的博客-CSDN博客

回溯

51. N 皇后

Leetcode 51. N皇后(回溯) —— python_missTu~的博客-CSDN博客

class Solution(object):
    def solveNQueens(self, n):
        board, ret = [['.'] * n for _ in range(n)], []
        self.dfs(board, n, 0, ret)
        return ret

    def dfs(self, board, n, row, ret):
        if row == n:
            ret.append(["".join(i) for i in board])
            return
        #核心方法是判断指定row和col能否放,能放就递归row+1的所有列,不能放就回溯
        for i in range(n):
            if not self.canPlace(row, i, n, board):
                continue
            board[row][i] = 'Q'
            self.dfs(board, n, row + 1, ret)
            board[row][i] = '.'

    #判断当前情况下能否放置皇后
    def canPlace(self, row, col, n, board):
        for i in range(1, row + 1):
            # 判断同一列上是否有Q
            if board[row - i][col] == 'Q':
                return False
            # 判断逆对角线是否有Q
            if col - i >= 0 and board[row - i][col - i] == 'Q':
                return False
            # 判断正对角线是否有Q
            if col + i < n and board[row - i][col + i] == 'Q':
                return False
        return True

39. 组合总和

class Solution(object):
    def combinationSum(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        if len(candidates)==0:return []
        ans=[]
        def backtrack(path,summ,index):
            if summ==target:
                ans.append(path)
            elif summ>target:
                return 
            elif summ<target:
                for i in range(index,len(candidates)):
                    backtrack(path+[candidates[i]],summ+candidates[i],i)
        backtrack([],0,0)
        return ans

class Solution(object):
    def helper(self, candidates, target, res, tmp, level):
        for i in range(level, len(candidates)):
            tmp.append(candidates[i])
            if candidates[i] < target:
                self.helper(candidates, target - candidates[i], res, tmp, i)
            elif candidates[i] == target:
                res.append(tmp[:])
                print(tmp)
            tmp.pop()

    def combinationSum(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        res = []
        self.helper(candidates, target, res, [], 0)
        return res

40. 组合总和 II

class Solution(object):
    def helper(self,candidates,target,res,tmp,level):
        for i in range(level,len(candidates)):
            tmp.append(candidates[i])
            if candidates[i]<target:
                self.helper(candidates,target-candidates[i],res,tmp,i+1)
            elif candidates[i]==target and tmp[:] not in res:
                res.append(tmp[:])
            tmp.pop()
    
    def combinationSum2(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        #与组合1不同的地方是,level往下循环不能包括现在的level.
        #要判断重复的情况
        candidates.sort()
        res=[]
        self.helper(candidates,target,res,[],0)
        return res

------------------------------------v2

注意两个地方,

1个是在深搜之前要进行排列,否则会出现[1,7],[7,1]这种情况。

2是要做剪枝操作。

if i>level and candidates[i]==candidates[i-1]:
    continue

不然重复元素太多的话会超时的。注意要i大于level的时候才跳过,不然就不是当前的层了。

class Solution(object):
    def helper(self, candidates, target, res, tmp, level):
        for i in range(level, len(candidates)):
            if i>level and candidates[i]==candidates[i-1]:
                continue
            tmp.append(candidates[i])
            if candidates[i] < target:
                self.helper(candidates, target - candidates[i], res, tmp, i + 1)
            elif candidates[i] == target and tmp[:] not in res:
                res.append(tmp[:])
            tmp.pop()
    def combinationSum2(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        candidates.sort()
        res = []
        self.helper(candidates, target, res, [], 0)
        return res

46. 全排列

class Solution(object):
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        if len(nums) == 0:
            return []
        res = []
        self.helper(nums, res, [], 0)
        return res
    def helper(self, nums, res, tmp, level):
        if len(tmp) == len(nums):
            res.append(tmp[:]) 
        for i in range(0, len(nums)):
            if nums[i] not in tmp:
                tmp.append(nums[i])
                self.helper(nums, res, tmp, i + 1)
                tmp.pop()

v2

class Solution(object):
    def helper(self, nums, res, tmp):
        if len(tmp) == len(nums):
            res.append(tmp[:])
        for i in range(0, len(nums)):
            if nums[i] not in tmp:
                tmp.append(nums[i])
                self.helper(nums, res, tmp)
                tmp.pop()
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        if len(nums) == 0:
            return []
        res = []
        self.helper(nums, res, [])
        return res

47 全排列2

用visited数组来防止往回走。

class Solution(object):
    def helper(self,nums,res,tmp,visited):
        if len(tmp)==len(nums) and tmp not in res:
            res.append(tmp[:])
        for i in range(0,len(nums)):
            if visited[i] == False:
                tmp.append(nums[i])
                visited[i] = True
                self.helper(nums,res,tmp,visited)
                tmp.pop()
                visited[i] = False
    def permuteUnique(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort()
        res=[]
        visited=[False for _ in range(len(nums))]
        self.helper(nums,res,[],visited)
        return res

78. 子集

leetcode 78. 子集(python回溯法)_vs974532452的博客-CSDN博客

class Solution(object):
    def subsets(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        if len(nums) == 0:
            return []
        res = []
        self.dfs(nums, res, [], 0)
        return res
    def dfs(self,nums,res,tmp,level):
        if len(tmp) <= len(nums):
            res.append(tmp[:]) 
        for i in range(level, len(nums)):
            tmp.append(nums[i])
            self.dfs(nums, res, tmp, i + 1)
            tmp.pop()

90 子集2

class Solution(object):
    def helper(self,nums,res,tmp,level):
        if len(tmp) <= len(nums) and tmp not in res:
            res.append(tmp[:])
        for i in range(level,len(nums)):
            if i>level and nums[i]==nums[i-1]:
                continue
            else:
                tmp.append(nums[i])
                self.helper(nums, res, tmp, i + 1)
                tmp.pop()
    def subsetsWithDup(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        if len(nums) == 0:
            return []
        res = []
        nums.sort()
        self.helper(nums, res, [], 0)
        return res

全排列组合总和子集问题总结

1.全排列问题,在for循环的时候是从0开始,组合和子集问题是从index开始

2.组合总和和子集问题,如果数据无序的,先排序。

3.全排列问题,如果有重复元素,可以用visited来防止往回遍历。

4.子集和组合总和问题,可在for循环中,num[i]==nums[i-1]来剪枝。注意开始位置。

深度优先搜索

301. 删除无效的括号

题解:

花花酱 LeetCode 301. Remove Invalid Parentheses - 刷题找工作 EP139_哔哩哔哩_bilibili

1.判断括号合法的条件有两个:

a.右括号数量小于等于左括号的数量(i<n-1)

b.右括号数量等于左括号的数量(i=n-1)

2.计算需要移除的左括号和右括号的数量:

a.根据条件1,从左往右扫,右括号数量大于左括号的数量。

b.遍历完了之后,左括号右括号多几个。

3.递归,先移除右括号,保证前缀合法,再移除左边括号。

力扣

class Solution:
    def removeInvalidParentheses(self, s: str) -> List[str]:
        # 首先确定多余的括号:
        cntleft, cntright = 0, 0
        for char in s:
            if char == "(":
                cntleft += 1
            if char == ")":
                if cntleft == 0:
                    cntright += 1
                else:
                    cntleft -= 1
        self.res = []
        self.dfs(s, 0, cntleft, cntright)
        return self.res
    
    # 以递归求解 helper 表示从start开始,移除s中的cntleft个和cntright个左/右括号,其实也是DFS不停向下遍历
    def dfs(self, s, start, cntleft, cntright):
        # 终止条件
        if cntleft==0 and cntright==0 and self.valid(s):
            self.res.append(s)
            return
        
        # 未达到终止条件,缩小问题:
        for i in range(start, len(s)):
             # 对于多个相同的半括号在一起,只删除第一个,算一次就行了
            if i>start and s[i]==s[i-1]:
                continue
            # 遍历s,在所有的左括号的地方,移除当前点的左括号,然后问题在当前点移除做括号后,缩小为从这个点开始,再移除后面的cntleft-1个和cntright个括号
            if s[i] == "(" and cntleft>0:
                self.dfs(s[0:i]+s[i+1:], i, cntleft-1, cntright)
            # 遍历s,在所有的右括号的地方,移除当前点的右括号,然后问题在当前点移除做括号后,缩小为从这个点开始,再移除后面的cntleft个和cntright-1个括号
            elif s[i] == ")" and cntright>0:
                self.dfs(s[0:i]+s[i+1:], i, cntleft, cntright-1)

    def valid(self, s):
        stack = []
        for char in s:
            if char not in ["(", ")"]:
                continue
            if char == "(":
                stack.append(")")
            elif len(stack) == 0:
                return False
            else:
                stack.pop()
        return len(stack)==0

作者:oliver8641
链接:https://leetcode-cn.com/problems/remove-invalid-parentheses/solution/301-remove-invalid-parentheses-by-oliver8641/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

BFS

力扣

class Solution:
    def removeInvalidParentheses(self, s: str) -> List[str]:
        from collections import deque
        res = set()
        # 删除左括号的个数
        rmL = 0
        # 删除右括号的个数
        rmR = 0
        for a in s:
            if a == "(":
                rmL += 1
            elif a == ")":
                if rmL != 0:
                    rmL -= 1
                else:
                    rmR += 1
        # 是否满足有效括号
        def isValid(s):
            cnt = 0
            for a in s:
                if a == "(":
                    cnt += 1
                elif a == ")":
                    cnt -= 1
                    if cnt < 0: return False
            return True
        # 记录此时 s , 左右括号的个数
        queue = deque([(s, rmL, rmR)])
        visited = set()
        visited.add((s, rmL, rmR))
        while queue:
            tmp_s, left_p, right_p = queue.pop()
            # 输出条件
            if left_p == 0 and right_p == 0 and isValid(tmp_s):
                res.add(tmp_s)
            for i in range(len(tmp_s)):
                # 为字母时候
                if tmp_s[i] not in "()": continue
                if tmp_s[i] == "(" and left_p > 0:
                    t = tmp_s[:i] + tmp_s[i + 1:]
                    if (t, left_p - 1, right_p) not in visited:
                        queue.appendleft((t, left_p - 1, right_p))
                        visited.add((t, left_p - 1, right_p))
                if tmp_s[i] == ")" and right_p > 0:
                    t = tmp_s[:i] + tmp_s[i + 1:]
                    if (t, left_p, right_p - 1) not in visited:
                        queue.appendleft((t, left_p, right_p - 1))
                        visited.add((t, left_p, right_p - 1))
        return list(res)


作者:powcai
链接:https://leetcode-cn.com/problems/remove-invalid-parentheses/solution/dfs-bfs-by-powcai-4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

22 括号生成

力扣

https://segmentfault.com/a/1190000022110082

cur路径单独append再pop的写法

Leetcode练习(Python):回溯算法类:第22题:括号生成:数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 - 桌子哥 - 博客园

在函数里传参的写法

Python|回溯算法解括号生成问题_算法与编程之美-CSDN博客

结合树结构图理解dfs的过程

from typing import List


class Solution:
    def generateParenthesis(self, n: int) -> List[str]:

        res = []
        cur_str = ''

        def dfs(cur_str, left, right, n):
            """
            :param cur_str: 从根结点到叶子结点的路径字符串
            :param left: 左括号已经使用的个数
            :param right: 右括号已经使用的个数
            :return:
            """
            if left == n and right == n:
                res.append(cur_str)
                return
            if left < right:
                return

            if left < n:
                dfs(cur_str + '(', left + 1, right, n)

            if right < n:
                dfs(cur_str + ')', left, right + 1, n)

        dfs(cur_str, 0, 0, n)
        return res

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/generate-parentheses/solution/hui-su-suan-fa-by-liweiwei1419/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

140. 单词拆分 II

回溯剪枝

不剪枝的话,直接回溯,会超时

class Solution(object):
    def __init__(self):
        self.res=[]
    def dfs(self,s,path,wordDict):
        if len(s)==0:
            self.res.append(path[:len(path)-1])
        for i in range(len(s)+1):
            cur=s[:i]
            if cur in wordDict:
                self.dfs(s[i:],path+cur+" ",wordDict)
    def wordBreak(self, s, wordDict):
        """
        :type s: str
        :type wordDict: List[str]
        :rtype: List[str]
        """
        self.dfs(s,"",wordDict)
        return self.res

剪枝,memo[i]代表了s[i:]这个字符串能否被分割,如果不能,那就停止后续的所有遍历。

力扣

class Solution(object):
    def __init__(self):
        self.res=[]
    def dfs(self,s,start,path,wordDict,memo):
        count=len(self.res)
        if start==len(s):
            self.res.append(path[:len(path)-1])
        for i in range(start,len(s)+1):
            cur=s[start:i]
            if cur in wordDict and memo[i]:
                self.dfs(s,i,path+cur+" ",wordDict,memo)
        if len(self.res)>count:
            memo[start]=1
        else:
            memo[start]=0
    def wordBreak(self, s, wordDict):
        """
        :type s: str
        :type wordDict: List[str]
        :rtype: List[str]
        """
        memo=[1 for _ in range(len(s)+1)]
        self.dfs(s,0,"",wordDict,memo)
        return self.res

139. 单词拆分

leetcode--139单词拆分 - 菜鸟学院

https://sexywp.com/139-word-break.htm

这里引入一个bool型的数组,用于记录我们之前已经在哪个位置进行过切割,而且后续不行的。

 

要加剪枝操作,在前面遍历的时候,把结果存一下,后面再遍历到这个位置的时候,就可以直接返回结果。

class Solution(object):
    def __init__(self):
        self.res= False
    def dfs(self,s,start,wordDict,memo):
        if start==len(s):
            self.res=True
        if self.res==True:
            return 
        for i in range(start,len(s)+1):
            cur=s[start:i]
            if cur in wordDict and memo[i]:
                self.dfs(s,i,wordDict,memo)
        if self.res:
            memo[start]=1
        else:
            memo[start]=0

    def wordBreak(self, s, wordDict):
        """
        :type s: str
        :type wordDict: List[str]
        :rtype: bool
        """
        memo=[1 for _ in range(len(s)+1)]
        self.dfs(s,0,wordDict,memo)
        if self.res:
            return True
        return False

638大礼包

leetcode每日一题202110_MaYingColdPlay的博客-CSDN博客

二叉树的路径总和

576. 出界的路径数

力扣

力扣

class Solution(object):
    def findPaths(self, m, n, maxMove, startRow, startColumn):
        """
        :type m: int
        :type n: int
        :type maxMove: int
        :type startRow: int
        :type startColumn: int
        :rtype: int
        """
        return self.dfs(m,n,maxMove,startRow,startColumn,{}) % (10**9+7)
        
    def dfs(self, m, n, k, i, j, cnt):
        if (k, i, j) in cnt:
            return cnt[(k, i, j)]
        if i < 0 or j < 0 or i == m or j == n:
            return 1
        if k == 0:
            return 0
        cnt[(k, i, j)] = 0
        for d in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
            ni, nj = i+d[0], j+d[1]
            cnt[(k, i, j)] += self.dfs(m, n, k-1, ni, nj, cnt)
        return cnt[(k, i, j)]

递归-找出不同的二进制字符串

力扣

class Solution(object):
    def __init__(self):
        self.res=''
    def dfs(self,cur_str,n,nums):
        if len(cur_str)==n:
            if cur_str not in nums:
                self.res=cur_str
            return 
        self.dfs(cur_str+'1',n,nums)
        self.dfs(cur_str+'0',n,nums)
    def findDifferentBinaryString(self, nums):
        """
        :type nums: List[str]
        :rtype: str
        """
        n=len(nums[0])
        self.dfs('',n,nums)
        return self.res

记忆化dfs

力扣

题解

力扣

看起来是dfs实际上是找规律的题

5891.找出缺失的观测数据

5891. 找出缺失的观测数据

这是一个周赛题,我做的时候以为是类似于组合问题的dfs,一直超时,剪枝剪了半天都通不过。看了答案才知道是找规律题。因为这个数量太大,递归肯定超时,而且只让输出一个,模拟就行了。

力扣https://leetcode-cn.com/problems/find-missing-observations/solution/wei-rao-li-lun-mo-ni-ti-zhi-jie-ping-jun-b2hm/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值