动手刷力扣第七天——二叉树(力扣94,144,145)

树相关知识:

树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。

它具有以下的特点:

1. 每个节点有零个或多个子节点;

2. 没有父节点的节点称为根节点;

3.每一个非根节点有且只有一个父节点;

4.除了根节点外,每个子节点可以分为多个不相交的子树。

一些关于树的基本专业术语:

结点的度:结点拥有的子树的数目。

叶子:度为零的结点。

分支结点:度不为零的结点。

树的度:树中结点的最大的度。

层次:根结点的层次为1,其余结点的层次等于该结点的双亲结点的层次加1。

树的高度:树中结点的最大层次。

无序树:如果树中结点的各子树之间的次序是不重要的,可以交换位置。

有序树:如果树中结点的各子树之间的次序是重要的, 不可以交换位置。

森林:0个或多个不相交的树组成。对森林加上一个根,森林即成为树;删去根,树即成为森林。

二叉树相关知识:

二叉树是每个节点最多有两个子树的树结构。具有如下性质:

性质1:二叉树第i层上的结点数目最多为 2{i-1} (i≥1)。

性质2:深度为k的二叉树至多有2{k}-1个结点(k≥1)。

性质3:包含n个结点的二叉树的高度至少为log2 (n+1)

性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1

满二叉树:高度为h,并且由2{h} –1个结点的二叉树,被称为满二叉树。

完全二叉树:一棵二叉树中,只有最下面两层结点的度可以小于2,并且最下一层的叶结点集中在靠左的若干位置上。一棵满二叉树必定是一棵完全二叉树,而完全二叉树未必是满二叉树。

二叉搜索树:要求每个节点本身大于其左子树,而小于其右子树,对其进行中序遍历后,会得到一个有序的列表,这是我们经常用到的一种数的结构。
平衡二叉树:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树,并且满足二叉搜索树的规则。

二叉树的遍历:分为前序遍历,中序遍历和后序遍历。

前序遍历:根结点 ---> 左子树 ---> 右子树

中序遍历:左子树---> 根结点 ---> 右子树

后序遍历:左子树 ---> 右子树 ---> 根结点

力扣94二叉树的中序遍历:

 对于这道题,我们可以分别采用递归和迭代两种解法来进行遍历。首先,递归是比较简单直观的。(这个视频对递归讲的很好,可以看看 二叉树递归路径图解_哔哩哔哩_bilibili

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]):
        result = []                 # 初始化一个数组记录结果
        self.helper(root, result)   # 定义辅助函数完成遍历
        return result
    
    def helper(self, node, result):
        if node is None:            # 节点是空直接return
            return
        self.helper(node.left, result)  # 递归遍历其左子树
        result.append(node.val)         # 先左再中间再右
        self.helper(node.right, result)
# 递归法(中序遍历)
# N是节点数,H是树的高度        
# time complexity: O(N)     
# space cOmplexity: O(H)

然后是迭代法:由于栈的先进后出原则,我们可以把树从根节点开始往左依次放进栈中,指针指向空即最左边已经没有左子树的节点,我们开始取出栈顶元素,由栈的性质保证左-根-右的特性输出。

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]):
        result = []     # 初始化一个数组记录结果
        stack = []      # 指定一个辅助的栈
        cur = root      # 指针从根节点开始
        while(cur or stack):  # 指针指向空节点或者栈为空停止循环   
            if cur:           # 只要不指向空节点,就执行下面的语句
                stack.append(cur)   # 把这个节点加入栈中
                cur = cur.left      # 指针指向该节点的左子树
            else:                   # 当指针指向空时,
                cur = stack.pop()   # 把栈顶元素取出
                result.append(cur.val)  # cur指向的值加入result中
                cur = cur.right     # 指针指向节点(栈顶元素)的右子树
        return result
# 迭代法(中序遍历)
# N是节点数,H是树的高度        
# time complexity: O(N)     
# space cOmplexity: O(H)

下面来看一下大佬写的颜色标记法(可以作为前中后序遍历的模板,只需调整三句代码的顺序即可分别完成前中后序遍历)

使用颜色标记节点的状态,新节点为白色,已访问的节点为灰色。如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点、自身、左子节点依次入栈。如果遇到的节点为灰色,则将节点的值输出。(详情可以看看力扣链接力扣

class Solution:
    def inorderTraversal(self, root: TreeNode):
        WHITE, GRAY = 0, 1          # 白遍历。灰输出
        result = []                    
        stack = [(WHITE, root)]     # 初始化栈放入白和根节点
        while stack:                # 栈不为空进入循环
            color, node = stack.pop()   # 取出栈顶元素
            if node is None: continue   # 如果节点为空继续执行
            if color == WHITE:          # 白,遍历,添加元素入栈
                stack.append((WHITE, node.right))   # 中序遍历左根右,入栈顺序相反            
                stack.append((GRAY, node))                
                stack.append((WHITE, node.left))
                
            else:
                result.append(node.val)    # 灰色加入结果集
        return result
# 颜色标记法(中序遍历)
# time complexity: O(N)     遍历了所有节点  
# space complexity: O(N)    产生了新的数组result

力扣144二叉树的前序遍历

对于这道题,我们和中序遍历一样采用递归法,迭代法和颜色标记法来做。只需交换部分代码顺序即可。

递归:

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]):
        result = []
        self.helper(root, result)
        return result
    
    def helper(self, node, result):
        if node is None:
            return
        result.append(node.val) 
        self.helper(node.left, result)      
        self.helper(node.right, result)

# 递归法(前序遍历)
# N是节点数,H是树的高度        
# time complexity: O(N)     
# space cOmplexity: O(H)

迭代:由于前序遍历是根-左-右的顺序,所以在把根节点保存后先右后左的顺序保存进栈中,取出时就会形成先左后右的结果。

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]):
        # 如果根节点为空,直接返回空列表
        if not root:
            return []

        stack = [root]          # 初始化栈加入根节点
        result = []             # 结果数组保存结果

        while stack:           # 当栈为空停止循环
            node = stack.pop()              # 节点入栈           
            result.append(node.val)         # 将根节点值加入结果      
            if node.right:                  # 如果右子树非空
                stack.append(node.right)    # 右子树入栈          
            if node.left:                   # 如果左子树非空
                stack.append(node.left)     # 左子树入栈
        return result
# 迭代法(前序遍历)

# N是节点数,H是树的高度        
# time complexity: O(N)      遍历了所有节点  
# space complexity: O(H)     产生了新的数组result

颜色标记法:

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]):
        WHITE, GRAY = 0, 1          # 白遍历。灰输出
        result = []                    
        stack = [(WHITE, root)]     # 初始化栈放入白和根节点
        while stack:                # 栈不为空进入循环
            color, node = stack.pop()   # 取出栈顶元素
            if node is None: continue   # 如果节点为空继续执行
            if color == WHITE:          # 白,遍历,添加元素入栈
                stack.append((WHITE, node.right))   # 前序遍历根左右,入栈顺序相反            
                stack.append((WHITE, node.left))
                stack.append((GRAY, node))
            else:
                result.append(node.val)    # 灰色加入结果集
        return result
    
# 颜色标记法(前序遍历)
# time complexity: O(N)     遍历了所有节点  
# space complexity: O(N)    产生了新的数组result

力扣145二叉树的后序遍历:

 递归:同样只是交换顺序即可

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]):
        result = []
        self.helper(root, result)
        return result
    
    def helper(self, node, result):
        if node is None:
            return
        
        self.helper(node.left, result)      
        self.helper(node.right, result)
        result.append(node.val) 

# 递归法(后序遍历)
# N是节点数,H是树的高度        
# time complexity: O(N)     
# space cOmplexity: O(H)

迭代:(画图比较好理解)

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]):
        # 如果根节点为空,直接返回空列表
        if not root:
            return []
        stack = []
        result = []
        while root or stack:
            while root:             # 当root不为None执行循环             
                stack.append(root)  # 当前节点入栈               
                if root.left:   # 如果当前节点有左子树,继续向左子树找
                    root = root.left
                else:           # 如果当前节点无左子树,在右子树继续找
                    root = root.right
        # 跳出循环的条件是 root 为空,那当前栈顶元素为叶子节点。
            cur = stack.pop()   # 弹出栈顶元素,并加入结果数组
            result.append(cur.val)

            # 如果栈不为空,且当前栈顶元素的左节点是刚刚跳出的栈顶元素 cur
            # 则转向遍历右子树当前栈顶元素的右子树,stack[-1]代表栈顶元素
            if stack and stack[-1].left == cur:
                root = stack[-1].right
            # 否则证明当前栈顶元素无左右子树,那当前的栈顶元素弹出。
            else:
                root = None

        return result
# 迭代法(后序遍历)

# N是节点数,H是树的高度        
# time complexity: O(N)      遍历了所有节点  
# space complexity: O(H)     产生了新的数组result

颜色标记法:(yyds,同样只需调换入栈顺序即可)

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]):
        # 如果根节点为空,直接返回空列表
        if not root:
            return []
        WHITE, GRAY = 0, 1          # 白遍历。灰输出
        result = []                    
        stack = [(WHITE, root)]     # 初始化栈放入白和根节点
        while stack:                # 栈不为空进入循环
            color, node = stack.pop()   # 取出栈顶元素
            if node is None: continue   # 如果节点为空继续执行
            if color == WHITE:          # 白,遍历,添加元素入栈           
                stack.append((GRAY, node))  # 后序遍历左右根,入栈顺序相反
                stack.append((WHITE, node.right))
                stack.append((WHITE, node.left))
            else:
                result.append(node.val)    # 灰色加入结果集
        return result
    
# 颜色标记法(前序遍历)
# time complexity: O(N)     遍历了所有节点  
# space complexity: O(N)    产生了新的数组result

这个二叉树的遍历前前后后看了一周了,虽然中途有很多其他事忙,最后还是写出来了,中途参考了很多大佬的答案与思路,总结出三种解法,好好学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值