算法(16)-leetcode-explore-learn-数据结构-二叉树总结

leetcode-explore-learn-数据结构-二叉树3

  • 1.根据中序和后序遍历序列构造二叉树
  • 2.根据前序和中序遍历序列构造二叉树
  • 3.填充每个节点的下一个右侧节点指针
  • 4.填充每个节点的下一个右侧节点指针2
  • 5.二叉树的最近公共祖先
  • 6.二叉树的序列化和反序列化

本系列博文为leetcode-explore-learn子栏目学习笔记,如有不详之处,请参考leetcode官网:https://leetcode-cn.com/explore/learn/card/data-structure-binary-tree/2/traverse-a-tree/7/

所有例题的编程语言为python
二叉树节点结构:

class TreeNode(object):
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

1.根据中序和后序遍历序列构造二叉树

inorder=[9,3,15,20,7] 左根右
postorder=[9,15,7,20,3] 左右根,逆序就是根右左:[3,20,7,15,9]
由后序遍历中可知根节点是3,在中序遍历中可以确定左右子树序列是多少。如何提取下一个根节点呢?取根和左右子树递归构造该如何衔接。

inorder 用来控制递归出口
postorder 才是提供根节点的源泉。

class Solution(object):
    def buildTree(self, inorder, postorder):
        """
        :type inorder: List[int]
        :type postorder: List[int]
        :rtype: TreeNode
        """
        global pos_index
        postorder.reverse()
        def helper(in_left=0,in_right=len(inorder)):
            global pos_index
            if in_left==in_right:
                return None
            root_val=postorder[pos_index]
            root=TreeNode(root_val)
            pos_index+=1
            in_index=inorder.index(root_val)
            root.right=helper(in_index+1,in_right)
            root.left=helper(in_left,in_index)
            return root
        pos_index=0
        return helper()

2.根据前序和中序遍历序列构造二叉树

preorder=[3,9,20,15,7] 中左右
inorder=[9,3,15,20,7] 左中右
一个根节点可以将中序遍历划分为左一半,右一半。
全局变量pre_idx,每次运行一次helper函数一次加1,取下一根节点;直至左子树运行完,对应的根节点下标也应该遍历完全了。
剩余问题:index不因该是根节点的在中序遍历中的下标么?左子树包含的内容不是应该为[0,index-1] 根递归出口有关,不是直接索引元素。

class Solution(object):
    def buildTree(self, preorder, inorder):
        """
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        global pre_index
        def helper(in_left=0,in_right=len(inorder)):
            global pre_index
            if in_left==in_right:
                return None
            root_val=preorder[pre_index]
            root=TreeNode(root_val)
            pre_index+=1
            
            in_index=inorder.index(root_val)
            root.left=helper(in_left,in_index)
            root.right=helper(in_index+1,in_right)
            
            return root
        pre_index=0
        return helper()

3.填充每个节点的下一个右侧节点指针

填充一个完美二叉树的每个解答的每个节点的下一个右侧节点。完美二叉树说的是,所有叶子节点都在同一层。

思路:关键找到每一个节点的下一个节点,那不就是二叉树的层次遍历。
每层的节点的next指针指其下一个节点,用l来控制该层的最后一个节点指向None。

class Solution(object):
    def connect(self, root):
        """
        :type root: Node
        :rtype: Node
        """
        if root==None:
            return None 
        que=[root]
        while(que):
            l=len(que)
            for i in range(l):
                node=que.pop(0)
                if i==l-1:
                    node.next=None
                else:
                    node.next=que[0]
                if node.left:
                    que.append(node.left)
                if node.right:
                    que.append(node.right)
        return root

4.填充每个节点的下一个右侧节点指针2

给定一个二叉树,填充它每个next指针指向右侧节点,同一层的最右侧节点填充为None.
思路:不是一棵完美的二叉树,不过还是树的层次遍历,上一题的框架依旧可以使用。
代码一点没改能过。

5.二叉树的最近公共祖先

给定一个二叉树,找到该树中两个指定节点的最近公共祖先。
官方思路1:递归
递归遍历整棵树,定义 f x f_x fx表示x节点的子树中是否包含p节点或者q节点,如果包含则为true.采用自底向上从叶子节点开始更新,保证满足条件的公共祖先深度最深。

class Solution(object):
    def __init__(self):
        self.ans=None
    def lowestCommonAncestor(self, root, p, q):
        def rec(node,p,q):
            if node==None:
                return False
            left=rec(node.left,p,q)  # 记录左子树的有没有待识别的点
            right=rec(node.right,p,q) # 记录右子树有没有待识别的点
            mid=(node==p or node==q)
            if mid+left+right==2:
                self.ans=node
            return mid or left or right
        rec(root,p,q)
        return self.ans

官方思路2:储存父节点
用hash表存储所有节点的父亲节点,然后利用节点的父亲节点的信息从p往上跳,直至根节点,记录已经访问过的节点;再从q节点开始不断往上跳,每次上跳一个节点就去p已访问的节点中寻找是否已经访问过该节点。第一次遇到的p已经访问的节点,则该节点为答案。
难点1:父亲节点hash表。{child1:root1,child2:root1},只要遍历过二叉树的所有节点,就可以实现这个。
难点2:从p开始,不断在父亲hash表中找父亲节点,直至找不到父亲节点的跟节点,将所有路径放入[]中。
技巧:还是将节点放进去。

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        def dfs(node):
            if node==None:
                return
            if node.left:
                fa_hash[node.left]=node
            if node.right:
                fa_hash[node.right]=node
            dfs(node.left)
            dfs(node.right)
        fa_hash={root:None}
        vis_hash={}
        dfs(root)        
        while (p!=None):
            # print(p)
            vis_hash[p]=True	# 要记得把自己放进去
            p=fa_hash[p]
        while(q!=None):
            if vis_hash.get(q):	# 记得自己也要验证
                return q
            q=fa_hash[q]

6.二叉树的序列化和反序列化

将二叉树序列化为一个字符串,将得到的字符串反序列化为二叉树。
说明:不要使用类成员/全局/静态变量来存储状态,序列化和反序列化算法应该是无状态的。–什么是无状态?

序列化和反序列化递归顺序一致就可以。

class Codec:

    def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
        def dfs_ser(node,string):
            if node==None:
                string+="None,"
                return string
            string+="{0},".format(node.val)
            string=dfs_ser(node.left,string)
            string=dfs_ser(node.right,string)
            return string
        return dfs_ser(root,"")
                
    def deserialize(self, data):
        """Decodes your encoded data to tree.
        
        :type data: str
        :rtype: TreeNode
        """
        def rec_deser(lis):
            if lis[0]=="None":
                lis.pop(0)
                return None
            root_val=lis[0]
            root=TreeNode(root_val) # 顶端
            lis.pop(0)
            root.left=rec_deser(lis) # 底端
            root.right=rec_deser(lis)
            return root
        lis=data.split(",")
        return rec_deser(lis)

你可能感兴趣的:(算法)