leetcode 树相关问题

使用递归和遍历解耦树问题

树的创建与基本的遍历,这是后面大部分问题的基础

创建树

class Node:
    def __init__(self,val):
        self.val = val
        self.right = None
        self.left = None

    def __repr__(self):
        return f'val: {self.val}, left: {self.left}, right: {self.right}'

class Tree:
    def __init__(self):
        self.root = None

    def add_element(self,node_val):
        node = Node(node_val)
        if self.root == None:
            self.root = node
            return
        queue = [self.root]
        while True:
            pop_node = queue.pop(0)
            if pop_node.left is None:
                pop_node.left = node
                return
            else:
                queue.append(pop_node.left)
            if pop_node.right is None:
                pop_node.right = node
                return
            else:
                queue.append(pop_node.right)

新建树结构:

tree =Tree()
for i in range(1,8):
    tree.add_element(i)
  1. pytrhon类内方法__repr__,可以支持类的可视化;
  2. 问题的解耦,Node叶子,包含了值和左右的节点,而Tree树包含了一个队列(先进先出)辅助构建整个树结构。
  3. 树建设好后不需要依靠队列,在内存中依靠指针构成图的数据结构。
  4. Tree类建设好后,该类内的全局变量一直被保存,每次搜索都是从root节点开始,[root]——>pop_node = root——>[root.right,root.left]——>pop_node=root.left——>[root.right,root.left.right,root.left.left],pop(0)之后会依次追加pop_node的left和right,让每一层满了之后才下一层。
  5. 每次左右都会遍历到,只是有先后次序关系,所以才会让所有的节点都安顺序遍历到。

遍历

bfs

def bfs(self):
    if self.root is None:
        return
    queue = [self.root]
    while queue:
        pop_node = queue.pop(0)
        print(pop_node.val,end=' ')
        if pop_node.left is not None:
            queue.append(pop_node.left)
        if pop_node.right is not None:
            queue.append(pop_node.right)
  1. 与层次化创建一样,依靠一个队列,打印当前节点,把左节点和右节点放进来准备下次的打印。

递归形式的遍历

都是先左再右,前中后只的是根前中后打印。

predfs

根左右

def predfs(self,root):
    if root is None:
        return
    print(root.val,end=' ')
    self.predfs(root.left)
    self.predfs(root.right)

indfs

左根右

def indfs(self,root):
    if root is None:
        return
    self.indfs(root.left)
    print(root.val, end=' ')
    self.indfs(root.right)

afterdfs

左右根

def afterdfs(self,root):
    if root is None:
        return
    self.afterdfs(root.left)
    self.afterdfs(root.right)
    print(root.val, end=' ')

循环形式的遍历(栈)

pre_stack

def pre_stack(self):
    '''
    root left right,因为是stack所以倒着入,出root打印,然后入right,再left
    :return:
    '''
    if self.root is None:
        return
    stack = [self.root]
    while stack:
        pop_node = stack.pop()
        print(pop_node.val,end=' ')
        if pop_node.right is not None:
            stack.append(pop_node.right)
        if pop_node.left is not None:
            stack.append(pop_node.left)

in_stack

def in_stack(self):
    stack = []
    cur = self.root
    #原因为了能让循环开始,开始的时候stack是空的
    while stack or cur:
        while cur:
            stack.append(cur)
            cur = cur.left
        pop_node = stack.pop()
        print(pop_node.val,end=' ')
        cur = pop_node.right
    return

after_stack

 def after_stack(self):
     # left right root ,考虑第一种实现非常方便,所以倒序过来
     if self.root is None:
         return
     stack = [self.root]
     res = []
     while stack:
         pop_node = stack.pop()
         # print(pop_node.val,end=' ')
         res.append(pop_node)
         if pop_node.left is not None:
             stack.append(pop_node.left)
         if pop_node.right is not None:
             stack.append(pop_node.right)
     print(res[::-1])
     return
  1. 建设树的过程是队,遍历使用栈,这里把队和栈的行为表现的非常明白。
    (1).队列公平的,循环pop(0)依次把pop_node子节点(左、右)的信息入栈,体现了bfs的公平性,本层遍历满后才会继续进行。
    (2).栈的特点是“先紧着自己人做完的原则”,以前序为例,入栈顺序:right,left。所以,先pop()出栈顶的root,此时left就是当前的root整个循环串接起来,直到把所有关于left的内容都遍历结束,开始遍历栈底的right(这个right在开始的时候就已经入栈)这里关键是对树的结构依靠了栈的方式进行了遍历,从root的地方把right和left入进来,而后开始循环注意是出栈的时候才把这个节点的相关节点入栈。
  2. 中序遍历最复杂,关键是这个方法不是先出栈root节点。需要先找到最左边节点。所以除了依靠一个stack,还要依靠一个cur指针。
  3. 内层的while找到最左边的节点后出栈指向该节点的right,再入循环(可能是左子树的右节点),使用cur很妙的地方在于如果没有right接点直接对stack进行pop。代码很自然的完成了left root right的任务(包括了左节点还有右节点的case),所以还是先把整个结构构建起来,细枝末节的任务就容易了

翻转二叉树 leetcode 226

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def invertTree(self, root: TreeNode) -> TreeNode:
    	if not root:
    		return
    	root.right,root.left = self.invertTree(root.left),self.invertTree(root.right)
    	return root
  • 注意这是python的语法,同时让两个内容进行互换。
  • 每一次都是以root为中轴进行翻转,所以依次进行递归。

二叉树的恢复

从前序与中序遍历中恢复二叉树 leetcode 105

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

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if not preorder or not inorder:
            return
        root = TreeNode(preorder[0])
        index = inorder.index(root.val)
        root.left = self.buildTree(preorder[1:index+1],inorder[:index])
        root.right = self.buildTree(preorder[index+1:],inorder[index+1:])
        return root
  • 必须要有中序这个信息,否则恢复不出来,除非是完全二叉树。
  • 一定要先知道递归的输入和输出是什么,输入是两个列表,输出是输的root

从后序与中序遍历中恢复二叉树 leetcode 106

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

class Solution:
    def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
        if not inorder or not postorder:
            return
        root = TreeNode(postorder[-1])
        index = inorder.index(root.val)
        root.left = self.buildTree(inorder[:index],postorder[:index])
        root.right =  self.buildTree(inorder[index+1:],postorder[index:-1])
        return root
  • 只要是任意一个list为空则是建树完成。
  • 然后倒换着查看序号即可。

树的回溯问题

二叉树的所有路径 leetcode 257

输入:

   1
 /   \
2     3
 \
  5

输出: ["1->2->5", "1->3"]

解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def binaryTreePaths(self, root: TreeNode) -> List[str]:

        def dfs(root,path,res):
            if not root:
                return

            path += str(root.val)
            if not root.left and not root.right:
                res.append(path)
                return
            path+='->'
            dfs(root.left,path,res)
            dfs(root.right, path, res)
        path=''
        res =[]
        dfs(root,path,res)
        return res
  • 最重要的是树的回溯方法,解空间只有两个,左和右,所以不用写循环去遍历,因为只有两个,如果root的左和右都为空的时候,则已经到底了
  • 输出的格式也有点困难,是字符串,而且还要带个->,所以直接用空字符串去接它,这也是本题目的精髓所在,用结束条件去截止一下。文本也有类似的参考价值。如果还有后续再加上这个东西。这个题目比较特别,如果不是一个的输出,pop很难定位。所以这个特殊问题记住用str。也避免了pop,每一次正好都回去了,而且都是新值。
  • 不用pop因为path是字符串,所以每次的实际地址都不同,python可变变量和不可变变量
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页