算法-树

一、二叉查找树-平衡树

1、前置知识:二叉树的深度

# 节点定义如下
# Definition for a binary tree node.
class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
n3 = TreeNode(3)
n9 = TreeNode(9)
n20 = TreeNode(20)
n15 = TreeNode(15)
n7 = TreeNode(7)

n3.left = n9
n3.right = n20
n20.left = n15
n20.right = n7
Solution().maxDepth(n3)

 

# 方法一:每到一个节点,深度就+1
class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        return self.get_dep(root, 1)

    def get_dep(self, node, depth):
        if not node:
            return depth - 1
        return max(self.get_dep(node.left,depth+1),self.get_dep(node.right,depth+1))


# 方法二:
class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if root is None:
            return 0
        else:
            left_depth = self.maxDepth(root.left)
            right_depth = self.maxDepth(root.right)
        return max(left_depth,right_depth)+1
算法解析:
终止条件: 当 root​ 为空,说明已越过叶节点,因此返回 深度 0 。
递推工作: 本质上是对树做后序遍历,从叶子节点一层层向上加
计算节点 root​ 的 左子树的深度 ,即调用 maxDepth(root.left);
计算节点 root​ 的 右子树的深度 ,即调用 maxDepth(root.right);
返回值: 返回 此树的深度 ,即 max(maxDepth(root.left), maxDepth(root.right)) + 1。
方法二解析

 

2、判断这个二叉树是否为平衡二叉树

# 方法一
class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        if not root:
            # 空树也是平衡树
            return True
        left_height = self.get_dep(root.left, 1)
        right_height = self.get_dep(root.right, 1)
        if abs(left_height-right_height) > 1:
            return False
        else:
            return self.isBalanced(root.left) and self.isBalanced(root.right)

    def get_dep(self, node, depth):
        if not node:
            return depth - 1
        return max(self.get_dep(node.left,depth+1),self.get_dep(node.right,depth+1))


# 方法二
class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        if not root:
            return True

        left_height = self.depth(root.left)
        right_height = self.depth(root.right)
        if abs(left_height - right_height) > 1:
            return False
        else:
            return self.isBalanced(root.left) and self.isBalanced(root.right)

    def depth(self, root):
        if not root: 
            return 0

        return max(self.depth(root.left), self.depth(root.right)) + 1

 

二、树的前中后序遍历

作者:z1m
链接:https://leetcode-cn.com/problems/binary-tree-preorder-traversal/solution/tu-jie-er-cha-shu-de-si-chong-bian-li-by-z1m/

来源:力扣(LeetCode)

# 前序遍历
# 递归
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        def tree(node):
            if not node:
                return
            res.append(node.val)
            tree(node.left)
            tree(node.right)
        tree(root)
        return res


# 迭代
class Solution:
    """
    它先将根节点 cur 和所有的左孩子入栈并加入结果中,直至 cur 为空,用一个 while 循环实现:
    然后,每弹出一个栈顶元素 tmp,就到达它的右孩子,再将这个节点当作 cur 重新按上面的步骤来一遍,直至栈为空。这里又需要一个 while 循环。
    """
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        
        cur, stack, res = root, [], []
        while cur or stack:
            while cur:  # 根节点和左孩子入栈
                res.append(cur.val)
                stack.append(cur)
                cur = cur.left
            tmp = stack.pop()  # 每弹出一个元素,就到达右孩子
            cur = tmp.right
        return res



# 中序遍历
# 递归
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        def tree(node):
            if not node:
                return
            tree(node.left)
            res.append(node.val)
            tree(node.right)
        tree(root)
        return res



# 迭代
class Solution:
    """
    和前序遍历的代码完全相同,只是在出栈的时候才将节点 tmp 的值加入到结果中。
    """
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        
        cur, stack, res = root, [], []
        while cur or stack:
            while cur:  # 根节点和左孩子入栈
                stack.append(cur)
                cur = cur.left
            tmp = stack.pop()
            res.append(cur.val)  # 出栈再加入结果
            cur = tmp.right
        return res


# 后续遍历
# 递归
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        def tree(node):
            if not node:
                return
            tree(node.left)
            tree(node.right)
            res.append(node.val)
        tree(root)
        return res



# 迭代
class Solution:
    """
    继续按照上面的思想,这次我们反着思考,节点 cur 先到达最右端的叶子节点并将路径上的节点入栈;
    然后每次从栈中弹出一个元素后,cur 到达它的左孩子,并将左孩子看作 cur 继续执行上面的步骤。
    最后将结果反向输出即可。参考代码如下:
    """
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        
        cur, stack, res = root, [], []
        while cur or stack:
            while cur:  # 先达最右端
                res.append(cur.val)
                stack.append(cur)
                cur = cur.right
            tmp = stack.pop()
            cur = tmp.left
        return res[::-1]

 

三、广度搜索算法

1、102. 二叉树的层序遍历

题目连接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal/

作者:JonnyHuang
链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/python3-er-cha-shu-ceng-xu-bian-li-by-jo-nlx3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

BFS(Breadth First Search)

JonnyHuang作者的思路

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        """
        步骤:
            1.遍历当前层级,把当前层的所有节点的值存到结果列表中
            2.再次遍历当前层级,把当前层的所有节点的下一层级(左右节点)添加到遍历列表中
            3.重复1、2步
        """
        if not root:
            return []

        queue = [root]  # 当前层级的所有节点,默认从根节点开始
        res = []  # 最终结果列表
        while queue:
            res.append([cur_node.val for cur_node in queue])  # 步骤1
            level = []  # 存储下一层级的所有节点
            for node in queue:
                # 步骤2
                if node.left:
                    level.append(node.left)
                if node.right:
                    level.append(node.right)
            queue = level
        return res

 

官方写法:

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []
        
        res = []
        stack = [root, ]  # 进行遍历的节点顺序
        while stack:
            level = []  # 每层的节点都存在一起
            n = len(stack)  # 每层需要处理的节点数
            for _ in range(n):
                node = stack.pop(0)  # 当前处理的节点
                level.append(node.val)
                if node.left:
                    stack.append(node.left)
                if node.right:
                    stack.append(node.right)
            if level:
                res.append(level)
        return res

 

2、107. 二叉树的层序遍历 II

https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii/

给定一个二叉树,返回其节点值自底向上的层序遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
        """
        步骤:
            1.遍历当前层级,把当前层的所有节点的值存到结果列表中
            2.再次遍历当前层级,把当前层的所有节点的下一层级(左右节点)添加到遍历列表中
            3.重复1、2步
            4.返回值可使用切片倒叙输出
        """
        if not root:
            return []

        queue = [root]  # 当前层级的所有节点,默认从根节点开始
        res = []  # 最终结果列表
        while queue:
            res.append([cur_node.val for cur_node in queue])  # 步骤1
            level = []  # 存储下一层级的所有节点
            for node in queue:
                # 步骤2
                if node.left:
                    level.append(node.left)
                if node.right:
                    level.append(node.right)
            queue = level
        return res[::-1]

 

3、199. 二叉树的右视图

https://leetcode-cn.com/problems/binary-tree-right-side-view/

给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def rightSideView(self, root: TreeNode) -> List[int]:
        """
        步骤:
            1.遍历当前层级,右视图即每层最右边的值,即只需要对每一层的列表的最后一个元素入结果列表就可以了
            2.再次遍历当前层级,把当前层的所有节点的下一层级(左右节点)添加到遍历列表中
            3.重复1、2步
        """
        if not root:
            return []

        queue = [root]  # 当前层级的所有节点,默认从根节点开始
        res = []  # 最终结果列表
        while queue:
            res.append([cur_node.val for cur_node in queue][-1])  # 步骤1
            level = []  # 存储下一层级的所有节点
            for node in queue:
                # 步骤2
                if node.left:
                    level.append(node.left)
                if node.right:
                    level.append(node.right)
            queue = level
        return res

 

四、深度搜索算法

1、102. 二叉树的层序遍历

https://leetcode-cn.com/problems/binary-tree-level-order-traversal/

上面102题,我们也可以使用深度搜索算法

DFS(Depth First Search)不是按照层次遍历的。为了让递归的过程中同一层的节点放到同一个列表中,在递归时要记录每个节点的深度 level。递归到新节点要把该节点放入 level 对应列表的末尾。

作者:edelweisskoko
链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/102-er-cha-shu-de-ceng-xu-bian-li-die-da-bg9v/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        """
        1.因为每层的节点值要分开记录,所以递归的参数除了节点node以外还需要当前层数level
        2.如果节点已为空,return,结束当前递归分支即可
        3.如果res的长度已经和当前层数level相等,说明res需要多加个位置了,因为level是res数组的索引,索引是一定比长度要小的,如果相等说明数组长度不够长了,得扩容
        4.把当前节点加到对应层的数组中去res[level].append(node.val)
        5.继续依次遍历左右字节点,层数level + 1
        6.返回res
        """
        res = []

        def dfs(node, level):
            if not node:
                return
            if len(res) == level:
                res.append([])
            res[level].append(node.val)
            dfs(node.left, level + 1)
            dfs(node.right, level + 1)

        dfs(root, 0)
        return res

 

2、207. 课程表

https://leetcode-cn.com/problems/course-schedule/
作者:jyd
链接:https://leetcode-cn.com/problems/course-schedule/solution/course-schedule-tuo-bu-pai-xu-bfsdfsliang-chong-fa/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        """
        三个标志位对应的状态
        i == 0 : 干净的,未被 DFS 访问
        i == -1:其他节点启动的 DFS 访问过了,路径没问题,不需要再访问了
        i == 1  :本节点启动的 DFS 访问过了,一旦遇到了也说明有环了
        """

        def dfs(i, adjacency, flags):
            if flags[i] == -1:
                return True
            if flags[i] == 1:
                return False
            flags[i] = 1
            for j in adjacency[i]:
                if not dfs(j, adjacency, flags):
                    return False
            flags[i] = -1
            return True

        # 初始化每个课程的依赖(列表)
        adjacency = [[] for _ in range(numCourses)]
        # 每个课程的标志位
        flags = [0 for _ in range(numCourses)]
        # 填充完成每个课程的依赖
        for cur, pre in prerequisites:
            adjacency[cur].append(pre)
        # 开始DFS去判断
        for i in range(numCourses):
            if not dfs(i, adjacency, flags):
                return False
        return True

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2021-11-17 21:17  我用python写Bug  阅读(76)  评论(0编辑  收藏  举报