【剑指offer】数据结构类--二叉树Tree--15题「pas fini」

概念理解

数据结构和算法——二叉树
二叉树的常见算法
python实现二叉树和它的七种遍历

前序遍历:父节点-左子树-右子树
中序遍历:左子树-父节点-右子树
后序遍历:左子树-右子树-父节点

二叉搜索树:对于树中的每个节点X,它的左子树中所有关键字值小于X的关键字值,而它的右子树中所有关键字值大于X的关键字值。

字典序:按照字典中出现的先后顺序进行排序

Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)

题目

004-重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

方法一:
以本题例子为例:
前序遍历序列: {1,2,4,7,3,5,6,8}
中序遍历序列: {4,7,2,1,5,3,8,6}
第一步:根结点为1
第二步:根结点在中序遍历序列中下标为3的位置,那么[0…2]就为左子树,[4…7]就为右子树
只不过现在build()参数中为2个数组,道理一样,维护2个数组的下标就行了。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回构造的TreeNode根节点
    def reConstructBinaryTree(self, pre, tin):
        # write code here
        if pre==[] or tin==[]:
            return None
        root = TreeNode(pre.pop(0)) #删除pre中第一个值并记为主节点
        index = tin.index(root.val) #记录tin中该主节点位置
        root.left = self.reConstructBinaryTree(pre, tin[:index])
        root.right = self.reConstructBinaryTree(pre, tin[index + 1:])
        return root

017-树的子结构

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
第一步:在树A中找到和树B的节点一样的节点R
第二步:判断树A以R为根节点的子树是不是包含树B一样的结构

class Solution:
    def HasSubtree(self, pRoot1, pRoot2):
        # write code here
        result = False
        if pRoot1 and pRoot2: #当Tree1和Tree2都不为零的时候,才进行比较。否则直接返回false
            if pRoot1.val == pRoot2.val: #以这个根节点为为起点判断是否包含Tree2
                result = self.compare(pRoot1, pRoot2)
            if not result: #如果找不到,那么就再去root的左儿子当作起点,去判断时候包含Tree2
                result = self.HasSubtree(pRoot1.left, pRoot2)
            if not result: #如果还找不到,那么就再去root的右儿子当作起点,去判断时候包含Tree2
                result = self.HasSubtree(pRoot1.right, pRoot2)
        return result
    
    def compare(self, pRoot1, pRoot2):
        if not pRoot2: # 如果Tree2已经遍历完了都能对应的上,返回true
             return True
        if not pRoot1: # 如果Tree2还没有遍历完,Tree1却遍历完了。返回false
             return False
        if pRoot1.val != pRoot2.val: # 如果其中有一个点没有对应上,返回false
             return False
        return self.compare(pRoot1.left,pRoot2.left) and self.compare(pRoot1.right,pRoot2.right) # 如果根节点对应的上,那么就分别去子节点里面匹配

018-二叉树的镜像

操作给定的二叉树,将其变换为源二叉树的镜像。

方法一:递归
时间复杂度:O(n), n为树节点的个数。每个节点只用遍历一次,所以为O(n)
空间复杂度:O(n), 每个节点都会在递归栈中存一次

class Solution:
    # 返回镜像树的根节点
    def Mirror(self, root):
        # write code here
        if not root:
            return None
        root.right, root.left = root.left, root.right # 交换左右子树
        self.Mirror(root.right)
        self.Mirror(root.left)
        return root

方法二:迭代

def Mirror(self, root):
    stack = root and [root]
    while stack:
        n = stack.pop()
        if n:
            n.left, n.right = n.right, n.left
            stack += n.right, n.left

022-从上往下打印二叉树

从上往下打印出二叉树的每个节点,同层节点从左至右打印。
方法一:队列
时间复杂度:O(n),二叉树的每个节点遍历一次
空间复杂度:O(n),二叉树的每个节点入队列一次

class Solution:
    # 返回从上到下每个节点值列表,例:[1,2,3]
    def PrintFromTopToBottom(self, root):
        # write code here
        from collections import deque
        if not root:
            return []
        queue = deque([root])
        res = []
        while queue:
            cur = queue.popleft()
            if cur:
                res.append(cur.val)
                queue.append(cur.left)
                queue.append(cur.right)
        return res

方法二:利用list实现对队列的模拟

class Solution:
    # 返回从上到下每个节点值列表,例:[1,2,3]
    def PrintFromTopToBottom(self, root):
        # write code here
        if not root:
            return []
        currentstack = [root] #将root节点放到栈中,以开始进行遍历
        res = []
        while currentstack:
            nextStack = []
            for i in currentstack:
                if i.left: nextStack.append(i.left)
                if i.right: nextStack.append(i.right)
                res.append(i.val)
            currentstack = nextStack
        return res

023-二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。

二叉搜索树的特点:对于树中的每个节点X,它的左子树中所有关键字值小于X的关键字值,而它的右子树中所有关键字值大于X的关键字值。

方法一:递归
时间复杂度:O(nlogn)
空间复杂度:O(logn)
BST的后序序列的合法序列是,对于一个序列S,最后一个元素是x (也就是根),如果去掉最后一个元素的序列为T,那么T满足:T可以分成两段,前一段(左子树)小于x,后一段(右子树)大于x,且这两段(子树)都是合法的后序序列。

class Solution:
    def VerifySquenceOfBST(self, sequence):
        # write code here
        # 增加IsBST函数是因为对于递归来说sequence为空可以作为终止条件,
        # 而对于判断BST而言 sequence为空是False
        def IsBST(lst):
            if not lst:
                return True
            root = lst[-1]
            for i in range(len(lst)): # 查找分界点
                if lst[i]>root:
                    break
            for j in range(i,len(lst)-1):
                if lst[j]<root:
                    return False
            return IsBST(lst[:i]) and IsBST(lst[i:-2])
        
        if not sequence:
            return False
        return IsBST(sequence)

024-二叉树中和为某一值的路径

输入一颗二叉树的根节点和一个整数,按字典序打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

字典序:按照字典中出现的先后顺序进行排序

方法一:递归
时间复杂度:O(n), 树的所有节点需要遍历一次
空间复杂度:O(n), 当树退化到链表时,递归空间为O(n)

class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        # write code here
        def subFindPath(root):
            if root:
                b.append(root.val)
                if not root.left and not root.right and sum(b)==expectNumber:
                    a.append(b[:]) #浅拷贝,若a.append(b),b改变后a中b的至也改变
                else:
                    subFindPath(root.right)
                    subFindPath(root.left)
                b.pop() # 删去当前叶节点,回到父节点去访问另一个子节点
        
        if not root:
            return None
        a,b = [],[]
        subFindPath(root)
        return sorted(a) # sorted()按照字典序排序

026-二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

方法一:中序遍历将结点放入链表,再构造双向链表。
时间复杂度:O(nlogn)
时间复杂度:O(logn)?
【注意】当在函数内定义函数时,可以共享变量,
若两函数分开定义,则用self共享变量

class Solution:
    def Convert(self, pRootOfTree):
        # write code here
        def midTraversal(root):
            if not root:
                return
            midTraversal(root.left)
            arr.append(root)
            midTraversal(root.right)
            
        if not pRootOfTree:
            return 
        arr = []
        midTraversal(pRootOfTree)
        for i,v in enumerate(arr[:-1]):
            v.right = arr[i+1]
            arr[i+1].left = v
        return arr[0]
class Solution:
    def Convert(self, pRootOfTree):
        # write code here
        if not pRootOfTree:
            return 
        self.arr = []
        self.midTraversal(pRootOfTree)
        for i,v in enumerate(self.arr[:-1]):
            v.right = self.arr[i+1]
            self.arr[i+1].left = v
        return self.arr[0]
    
    def midTraversal(self, root):
        if not root:
            return
        self.midTraversal(root.left)
        self.arr.append(root)
        self.midTraversal(root.right)

方法二:Morris Traversal
时间复杂度:O(n)
空间复杂度:O(1)

class Solution:
    def Convert(self, pRootOfTree):
        # write code here
        # 1. 如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点q,将其右孩子连接为当前节点,同时设当前节点的左孩子为空以跳出循环,节点转移至其左孩子。
        # 2. 当前节点的左孩子设置为其前驱节点prev,若无则置为None。
        # 3. 若前驱节点prev为None,则当前节点为链表头;
        #	 若不为None,则前驱节点prev的右孩子设为当前节点。
        # 4. 前驱节点prev设为当前节点,当前节点转移至其右孩子。
        # 5. 重复以上 1234 直到当前节点为空。
        if not pRootOfTree:
            return
        cur = pRootOfTree
        pre = ans = None
        while cur:
            while cur.left:
                q = cur.left
                while q.right:
                    q = q.right
                q.right = cur # q为左侧叶结点
                cur.left, cur = None, cur.left
            cur.left = pre # 左侧叶结点指向前一个需要相连的节点
            if not pre:
                ans = cur # 找到链表的头,只执行一次
            else:
                pre.right = cur # 前一个需要相连的节点指向该左侧叶结点
            pre, cur = cur, cur.right
        return ans

038-二叉树的深度

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

039-平衡二叉树

057-二叉树的下一个结点

058-对称的二叉树

059-按之字形顺序打印二叉树

060-把二叉树打印成多行

061-序列化二叉树

062-二叉搜索树的第k个结点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值