【编程之路】面试必刷TOP101:二叉树系列(23-30,Python实现)

【面试必刷TOP101】系列包含:


23.二叉树的前序遍历

23.1 递归法

# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
#
class Solution:
    def preorder(self, list: List[int], root: TreeNode):
        if root == None:
            return None
        list.append(root.val)
        self.preorder(list, root.left)
        self.preorder(list, root.right)
    def preorderTraversal(self , root: TreeNode) -> List[int]:
        list = []
        self.preorder(list, root)
        return list

时间复杂度:O(n),其中 n 为二叉树的节点数,遍历二叉树所有节点。
空间复杂度:O(n),最坏情况下二叉树化为链表,递归栈深度为 n。

23.2 借助栈

在这里插入图片描述

class Solution:      
    def preorderTraversal(self , root: TreeNode) -> List[int]:
        if root == None:
            return None
        a, b = [], []
        a.append(root)
        while a:
            res = a.pop()
            b.append(res.val)
            if res.right != None:
                a.append(res.right)
            if res.left != None:
                a.append(res.left)
        return b

时间复杂度:O(n),其中 n 为二叉树的节点数,遍历二叉树所有节点。
空间复杂度:O(n),辅助栈空间最大为链表所有节点数。

24.二叉树的中序遍历

24.1 递归法

import sys
class Solution:
    def inorder(self, a: List[int], root: TreeNode):
        if root == None:
            return
        self.inorder(a, root.left)
        a.append(root.val)
        self.inorder(a, root.right)
    def inorderTraversal(self , root: TreeNode) -> List[int]:
        sys.setrecursionlimit(1500)
        a = []
        self.inorder(a, root)
        return a

时间复杂度:O(n),其中 n 为二叉树的节点数,遍历二叉树所有节点。
空间复杂度:O(n),最坏情况下二叉树化为链表,递归栈深度为 n。

24.2 借助栈

在这里插入图片描述

import sys
class Solution:
    def inorderTraversal(self , root: TreeNode) -> List[int]:
        a, b = [], []
        if root == None:
            return
        while root or b:
            while root:
                b.append(root)
                root = root.left
            res = b.pop()
            a.append(res.val)
            root = res.right
        return a

时间复杂度:O(n),其中 n 为二叉树的节点数,遍历二叉树所有节点。
空间复杂度:O(n),辅助栈空间最大为链表所有节点数。

25.二叉树的后序遍历

25.1 递归法

class Solution:
    def postorder(self, a: List[int], root: TreeNode):
        if root == None:
            return None
        self.postorder(a, root.left)
        self.postorder(a, root.right)
        a.append(root.val)
    def postorderTraversal(self , root: TreeNode) -> List[int]:
        a = []
        self.postorder(a, root)
        return a

时间复杂度:O(n),其中 n 为二叉树的节点数,遍历二叉树所有节点。
空间复杂度:O(n),最坏情况下二叉树化为链表,递归栈深度为 n。

25.2 借助栈

class Solution:
    def postorderTraversal(self , root: TreeNode) -> List[int]:
        if root == None:
            return
        a, b = [], []
        pre = None
        while root or a:
            while root:
                a.append(root)
                root = root.left
            res = a.pop()
            if res.right == None or res.right == pre:
                b.append(res.val)
                pre = res
            else:
                a.append(res)
                root = res.right
        return b

时间复杂度:O(n),其中 n 为二叉树的节点数,遍历二叉树所有节点。
空间复杂度:O(n),辅助栈空间最大为链表所有节点数。

26.二叉树的层序遍历

26.1 借助队列

import queue
class Solution:
    def levelOrder(self , root: TreeNode) -> List[List[int]]:
        res = []
        if root == None:
            return
        q = queue.Queue()
        q.put(root)
        cur = None
        while not q.empty():
            row = []
            n = q.qsize()
            for i in range(n):
                cur = q.get()
                row.append(cur.val)
                if cur.left:
                    q.put(cur.left)
                if cur.right:
                    q.put(cur.right)
            res.append(row)
        return res

时间复杂度:O(n),其中 n 为二叉树的节点数,每个节点访问一次。
空间复杂度:O(n),队列的空间为二叉树的一层的节点数,最坏情况二叉树的一层为 O(n) 级。

26.2 递归

step 1:首先判断二叉树是否为空,空树没有遍历结果。
step 2:使用递归进行层次遍历输出,每次递归记录当前二叉树的深度,每当遍历到一个节点,如果为空直接返回。
step 3:如果遍历的节点不为空,输出二维数组中一维数组的个数(即代表了输出的行数)小于深度,说明这个节点应该是新的一层,我们在二维数组中增加一个一维数组,然后再加入二叉树元素。
step 4:如果不是step 3的情况说明这个深度我们已经有了数组,直接根据深度作为下标取出数组,将元素加在最后就可以了。
step 5:处理完这个节点,再依次递归进入左右节点,同时深度增加。因为我们进入递归的时候是先左后右,那么遍历的时候也是先左后右,正好是层次遍历的顺序。

import sys
sys.setrecursionlimit(100000)
class Solution:
    res = []
    def traverse(self, root: TreeNode, depth: int):
        if root:
            # 新的一层
            if len(self.res) < depth:
                row = []
                self.res.append(row)
                row.append(root.val)
            # 读取该层的一维数组,将元素加入到末尾
            else:
                row = self.res[depth-1]
                row.append(root.val)
        else:
            return
        # 递归时左右深度要加 1
        self.traverse(root.left, depth+1)
        self.traverse(root.right, depth+1)
    def levelOrder(self , root: TreeNode) -> List[List[int]]:
        if root == None:
            return self.res
        self.traverse(root, 1)
        return self.res

时间复杂度:O(n),其中 n 为二叉树的节点数,每个节点访问一次。
空间复杂度:O(n),最坏二叉树退化为链表,递归栈的最大深度为 n。

27.按之字形顺序打印二叉树

27.1 借助队列

step 1:首先判断二叉树是否为空,空树没有打印结果。
step 2:建立辅助队列,根节点首先进入队列。不管层次怎么访问,根节点一定是第一个,那它肯定排在队伍的最前面,初始化flag变量。
step 3:每次进入一层,统计队列中元素的个数,更改flag变量的值。因为每当访问完一层,下一层作为这一层的子节点,一定都加入队列,而再下一层还没有加入,因此此时队列中的元素个数就是这一层的元素个数。
step 4:每次遍历这一层这么多的节点数,将其依次从队列中弹出,然后加入这一行的一维数组中,如果它们有子节点,依次加入队列排队等待访问。
step 5:访问完这一层的元素后,根据flag变量决定将这个一维数组直接加入二维数组中还是反转后再加入,然后再访问下一层。

import queue
class Solution:
    def Print(self , pRoot: TreeNode) -> List[List[int]]:
        res = []
        if pRoot == None:
            return
        q = queue.Queue()
        q.put(pRoot)
        cur = None
        flag = True
        while not q.empty():
            row = []
            n = q.qsize()
            flag = not flag
            for i in range(n):
                cur = q.get()
                row.append(cur.val)
                if cur.left:
                    q.put(cur.left)
                if cur.right:
                    q.put(cur.right)
            if flag:
                row = list(reversed(row))
            res.append(row)
        return res

时间复杂度:O(n),每个节点访问一次,因为reverse的时间复杂度为O(n),按每层元素reverse也相
当于O(n)。
空间复杂度:O(n),队列的空间最长为O(n)。

27.2 双栈法

step 1:首先判断二叉树是否为空,空树没有打印结果。
step 2:建立两个辅助栈,每次依次访问第一个栈s1与第二个栈s2,根节点先进入s1。
step 3:依据依次访问的次序,s1必定记录的是奇数层,访问节点后,将它的子节点(如果有)依据先左后右的顺序加入s2,这样s2在访问的时候根据栈的先进后出原理就是右节点先访问,正好是偶数层需要的从右到左访问次序。偶数层则正好相反,要将子节点(如果有)依据先右后左的顺序加入s1,这样在s1访问的时候根据栈的先进后出原理就是左节点先访问,正好是奇数层需要的从左到右访问次序。
step 4:每次访问完一层,即一个栈为空,则将一维数组加入二维数组中,并清空以便下一层用来记录。

class Solution:
    def Print(self , pRoot: TreeNode) -> List[List[int]]:
        head = pRoot
        res = []
        if head is None:
            return res
        a, b = [], []
        a.append(head)
        while a or b:
            temp = []
            # 遍历奇数层
            while a:
                node = a[-1]
                temp.append(node.val)
                if node.left:
                    b.append(node.left)
                if node.right:
                    b.append(node.right)
                a.pop()
            # 数组不为空才添加
            if len(temp):
                res.append(temp)
            # 清空本层数据
            temp = []
            while b:
                node = b[-1]
                temp.append(node.val)
                if node.right:
                    a.append(node.right)
                if node.left:
                    a.append(node.left)
                b.pop()
            if len(temp):
                res.append(temp)
        return res

时间复杂度:O(n),其中 n 为二叉树的节点数,遍历二叉树的每个节点。
空间复杂度:O(n),两个栈的空间最坏情况为 n。

28.二叉树的最大深度

28.1 深度递归

class Solution:
    def maxDepth(self , root: TreeNode) -> int:
        if root == None:
            return 0
        return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1

时间复杂度:O(N), 每个节点需要遍历一次。
空间复杂度:O(N),最坏情况树退化为链表。

28.2 层序遍历

import queue
class Solution:
    def maxDepth(self , root: TreeNode) -> int:
        if root == None:
            return 0
        res = 0
        q = queue.Queue()
        q.put(root)
        while not q.empty():
            n = q.qsize()
            for i in range(n):
                node = q.get()
                if node.left:
                    q.put(node.left)
                if node.right:
                    q.put(node.right)
            res = res + 1
        return res

时间复杂度:O(n),其中 n 为二叉树的节点数,遍历整棵二叉树。
空间复杂度:O(n),辅助队列的空间最坏为 n。

29.二叉树中和为某一值的路径

29.1 递归

step 1:每次检查遍历到的节点是否为空节点,空节点就没有路径。
step 2:再检查遍历到是否为叶子节点,且当前sum值等于节点值,说明可以刚好找到。
step 3:检查左右子节点是否可以有完成路径的,如果任意一条路径可以都返回true,因此这里选用两个子节点递归的或。

class Solution:
    def hasPathSum(self , root: TreeNode, sum: int) -> bool:
        if root == None:
            return False
        if root.left == None and root.right == None and root.val == sum:
            return True
        return self.hasPathSum(root.left, sum-root.val) or self.hasPathSum(root.right, sum-root.val)

时间复杂度:O(n),其中 n 为二叉树所有节点,前序遍历二叉树所有节点。
空间复杂度:O(n),最坏情况二叉树化为链表,递归栈空间最大为 n。

29.2 非递归(深度优先搜索+辅助栈)

在这里插入图片描述

class Solution:
    def hasPathSum(self , root: TreeNode, sum: int) -> bool:
        # 空结点找不到路径
        if root == None:
            return False
        # 栈辅助深度优先遍历
        s = []
        # 根结点入栈
        s.append((root,root.val))
        while len(s):
            temp = s[-1]
            s.pop()
            # 如果它是叶子结点,且路径和等于 sum
            if temp[0].left == None and temp[0].right == None and temp[1] == sum:
                return True
            # 左结点入栈
            if temp[0].left:
                s.append((temp[0].left, temp[1]+temp[0].left.val))
            # 右结点入栈
            if temp[0].right:
                s.append((temp[0].right, temp[1]+temp[0].right.val))
        return False

时间复杂度:O(n),其中 n 为二叉树所有节点,遍历二叉树所有节点。
空间复杂度:O(n),最坏情况二叉树化为链表,递归栈空间最大为 n。

30.二叉搜索树与双向链表

30.1 递归中序遍历

step 1:创建两个指针,一个指向题目中要求的链表头(head),一个指向当前遍历的前一节点(pre)。
step 2:首先递归到最左,初始化head与pre。
step 3:然后处理中间根节点,依次连接pre与当前节点,连接后更新pre为当前节点。
step 4:最后递归进入右子树,继续处理。
step 5:递归出口即是节点为空则返回。
在这里插入图片描述

class Solution:
    head = None # 指向题目要求的链表头
    pre = None # 指向当前遍历的前一结点
    def Convert(self, pRootOfTree):
        # 中序递归,叶子为空则返回
        if pRootOfTree == None:
            return None
        # 首先递归到最左最小值
        self.Convert(pRootOfTree.left)
        # 找到最小值后,初始化 head 和 pre
        if self.pre == None:
            self.head = pRootOfTree
            self.pre = pRootOfTree
        # 当前结点与上一结点建立连接,将 pre 设置为当前值
        else:
            self.pre.right = pRootOfTree
            pRootOfTree.left = self.pre
            self.pre = pRootOfTree
        self.Convert(pRootOfTree.right)
        return self.head

30.2 非递归中序遍历

step 1:创建两个指针,一个指向题目中要求的链表头(head),一个指向当前遍历的前一节点(pre),创建一个布尔型变量,标记是否是第一次到最左,因为第一次到最左就是链表头。
step 2:判断空树不能连接。
step 3:初始化一个栈辅助中序遍历。
step 4:依次将父节点加入栈中,直接进入二叉树最左端。
step 5:第一次进入最左,初始化head与pre,然后进入它的根节点开始连接。
step 6:最后将右子树加入栈中,栈中依次就弹出“左中右”的节点顺序,直到栈为空。

class Solution:
    def Convert(self, pRootOfTree):
        if pRootOfTree == None:
            return None
        # 设置栈用于遍历
        s = []
        head = None
        pre = None
        # 确认第一个遍历到最左,即为首位
        isFirst = True
        while s or pRootOfTree:
            # 走到最左
            while pRootOfTree:
                s.append(pRootOfTree)
                pRootOfTree = pRootOfTree.left
            pRootOfTree = s[-1]
            s.pop()
            # 首位
            if isFirst:
                head = pRootOfTree
                pre = pRootOfTree
                isFirst = False
            # 当前结点与上一结点建立连接,将 pre 设置为当前值
            else:
                pre.right = pRootOfTree
                pRootOfTree.left = pre
                pre = pRootOfTree
            pRootOfTree = pRootOfTree.right
        return head

时间复杂度:O(n),其中 n 为二叉树节点数,中序遍历二叉树所有节点。
空间复杂度:O(n),栈 s 最大空间为为 O(n)。

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

G皮T

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值