【想要九九六,天天刷力扣】深度优先算法 DFS | 递归算法 (一般用于树TreeNode结构)

DFS概述

众所周知,DFS是面试考察重点之一!

深度优先搜索属于图算法的一种,是一个针对图和树的遍历算法,英文缩写为DFS即Depth First Search,如下图所示。
在这里插入图片描述

下面列举了使用到DFS的题目:

题目

从根节点到叶节点的路径数字之和

给定一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。

每条从根节点到叶节点的路径都代表一个数字:

例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123

计算从根节点到叶节点生成的 所有数字之和

  • 解题思路:

深度优先算法,递归调用dfs
对于以下情况:

  1. 叶子节点:最终结果 += 当前值*10 + 叶子值
  2. 非叶子节点:继续dfs
  • 实现代码:
class Solution:
        def dfs(node,num):
            isLeaf = True
            num *= 10
            num+=node.val
            if node.left: dfs(node.left,num);isLeaf=False
            if node.right: dfs(node.right,num);isLeaf=False
            if isLeaf:self.ans+=num
            return
        self.ans = 0
        dfs(root,0)
        return self.ans
  • 细节:
    递归中可以先判断是否是叶子节点
    也可以存一个布尔值进行判断

单值二叉树

如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。
只有给定的树是单值二叉树时,才返回 true;否则返回 false。

  • 解题思路

简单深度优先算法实例

  • 实现代码
class Solution:
    def isUnivalTree(self, root: TreeNode) -> bool:
        dfs = lambda node: not node or node.val==root.val and dfs(node.left) and dfs(node.right)
        return dfs(root)

细节:
利用lambda函数可以tricky实现深度优先算法

根据二叉树创建字符串

你需要采用前序遍历的方式,将一个二叉树转换成一个由括号和整数组成的字符串。
空节点则用一对空括号 “()” 表示。而且你需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。

  • 解题思路

前序遍历和中序遍历本质都是
深度优先遍历+分情况讨论

  • 情况1:叶子节点
    直接返回叶子节点值

  • 情况2:左有右无
    省略右括号

  • 情况3:左无右有
    左括号不能省略

  • 情况4:左右都有:
    左右均遍历

可以发现情况3、4是可以进行合并的

  • 实现代码
class Solution:
    def tree2str(self, root: Optional[TreeNode]) -> str:
        dfs = self.tree2str
        if not root:return ""    
        if not root.left and not root.right:return str(root.val)
        if not root.right:return f"{root.val}({dfs(root.left)})"
        return f"{root.val}({dfs(root.left)})({dfs(root.right)})"

细节:
其中f-string可参照博客中python tricky技巧查看:
https://blog.csdn.net/Ejzq1/article/details/123584412

两数之和 IV 输入 BST

给定一个二叉搜索树 root 和一个目标结果 k,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。

  • 解题思路

深度优先遍历整个二叉树,使用python的set进行存储
由于只有两个数,直接将remainder添加到set中
如果是多个数就需要把不同情况的remainder加到set中

  • 实现代码:
class Solution:
    def findTarget(self, root: Optional[TreeNode], k: int) -> bool:
        s=set()
        def dfs(node):
            if not node:return False
            if node.val in s:return True
            s.add(k-node.val)
            return dfs(node.left) or dfs(node.right)
        return dfs(root)

细节:
python 中 dict 与 set 都是以hash方式存储的
时间复杂度为O(1)

左叶子之和

给定二叉树的根节点 root ,返回所有左叶子之和。

  • 解题思路

标准DFS遍历即可

  • 实现代码
class Solution:
    def sumOfLeftLeaves(self, root: TreeNode) -> int:
        def dfs(node,isLeft):
            if not node:return 0
            if isLeft and not node.left and not node.right:return node.val
            return dfs(node.left,True)+dfs(node.right,False)
        return dfs(root,False)
  • 细节:

dfs传入是否是左节点即可
同理,求右节点的和也一样处理

前序遍历构造二叉搜索树

给定二叉树的根节点 root ,返回所有左叶子之和。

  • 解题思路

给定一个整数数组,它表示BST(即 二叉搜索树 )的 先序遍历 ,构造树并返回其根。

保证 对于给定的测试用例,总是有可能找到具有给定需求的二叉搜索树。

二叉搜索树 是一棵二叉树,其中每个节点, Node.left 的任何后代的值 严格小于 Node.val , Node.right 的任何后代的值 严格大于 Node.val。

二叉树的 前序遍历 首先显示节点的值,然后遍历Node.left,最后遍历Node.right。

  • 实现代码
class Solution:
    def bstFromPreorder(self, preorder: List[int]) -> Optional[TreeNode]:
        def dfs(left,right):
            if left==right:return None
            root = TreeNode(preorder[left])
            mid = bisect.bisect(preorder[left:right],root.val)+left
            root.left = dfs(left+1,mid)
            root.right = dfs(mid,right)
            return root
        return dfs(0,len(preorder))
  • 细节:

bisect.bisect函数:
在有序列表 seq 中查找 item 的位置,并且返回其 索引 (index),使得插入item后序列依然保持有序
底层实现是二分查找

迷你语法分析器

给定一个字符串 s 表示一个整数嵌套列表,实现一个解析它的语法分析器并返回解析的结果 NestedInteger 。

列表中的每个元素只可能是整数或整数嵌套列表

  • 解题思路:
    嵌套 -> 递归 -> 深度优先算法

  • 实现代码:

class Solution:
    def deserialize(self, s: str) -> NestedInteger:
        index = 0
        def dfs() -> NestedInteger:
            nonlocal index
            if s[index] == '[':
                index += 1
                ni = NestedInteger()
                while s[index] != ']':
                    ni.add(dfs())
                    if s[index] == ',':index += 1   
                index += 1
                return ni
            else:
                negative = False if s[index] != '-' else (index := index+1)!=0 or True
                start = index
                while index < len(s) and s[index].isdigit(): index += 1
                num = int(s[start:index])
                return NestedInteger(-num if negative else num)

        return dfs()
  • 细节:

python 3.8 支持 海象运算符 :=

代码简化:

negative = False if s[index] != '-' else (index := index+1)!=0 or True

原代码:

if s[index] != '-':
	negative = False
else:
	index+=1
	negative = True

建立四叉树

给你一个 n * n 矩阵 grid ,矩阵由若干 0 和 1 组成。请你用四叉树表示该矩阵 grid 。

你需要返回能表示矩阵的 四叉树 的根结点。

注意,当 isLeaf 为 False 时,你可以把 True 或者 False 赋值给节点,两种值都会被判题机制 接受 。

四叉树数据结构中,每个内部节点只有四个子节点。此外,每个节点都有两个属性:

val:储存叶子结点所代表的区域的值。1 对应 True,0 对应 False;
isLeaf: 当这个节点是一个叶子结点时为 True,如果它有 4 个子节点则为 False 。

class Node {
    public boolean val;
    public boolean isLeaf;
    public Node topLeft;
    public Node topRight;
    public Node bottomLeft;
    public Node bottomRight;
}
  • 解题思路:
    一般建立树的题目都是使用递归算法

  • 实现代码:

class Solution:
    def construct(self, grid: List[List[int]]) -> 'Node':
        def dfs(rowLeft,rowRight,colLeft,colRight):
            if all(grid[i][j]==grid[rowLeft][colLeft] for i in range(rowLeft,rowRight) for j in range(colLeft,colRight)):return Node(grid[rowLeft][colLeft]==1,True)
            rowMid = (rowLeft+rowRight)//2
            colMid = (colLeft+colRight)//2
            return Node(
                True,
                False,
                dfs(rowLeft,rowMid,colLeft,colMid),
                dfs(rowLeft,rowMid,colMid,colRight),
                dfs(rowMid,rowRight,colLeft,colMid),
                dfs(rowMid,rowRight,colMid,colRight),
            )
        return dfs(0,len(grid),0,len(grid[0]))
  • 细节:

all() 函数只有里面的都是真才为真
也可以使用前缀和的方法保存preSum
但是可读性--

两棵二叉搜索树中的所有元素

给你 root1 和 root2 这两棵二叉搜索树。请你返回一个列表,其中包含 两棵树 中的所有整数并按 升序 排序。.

  • 解题思路:
    中序遍历:二叉搜索树
    归并排序:两个有序列表

  • 实现代码:

class Solution:
    def getAllElements(self, root1: TreeNode, root2: TreeNode) -> List[int]:
        def inorder(node: TreeNode, res: List[int]):
            if node:
                inorder(node.left, res)
                res.append(node.val)
                inorder(node.right, res)

        inorder(root1, nums1:=[])
        inorder(root2, nums2:=[])

        merged = []
        p1, n1 = 0, len(nums1)
        p2, n2 = 0, len(nums2)
        while True:
            if (f:=p1 == n1) or p2 == n2:
                merged.extend(nums2[p2:] if f else nums1[p1:])
                break
            if nums1[p1] < nums2[p2]:
                merged.append(nums1[p1])
                p1 += 1
            else:
                merged.append(nums2[p2])
                p2 += 1
        return merged
  • 细节:
    中序遍历只需要节点判空一次即可
    原地修改数组
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赛文X

觉得不错就打赏下呗mua~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值