day1: 二叉树

1. 二叉树的层序遍历

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。

示例:
二叉树:[3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回其层次遍历结果:

[
  [3],
  [9,20],
  [15,7]
]

方法1:  这种方法 不好

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

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        # 层序遍历 按层访问
        # 队列  FIFO
        queue_node = [root]
        node_list = []
        while queue_node:  # 队列非null, 一直循环
            # 处理 根节点
            queue_len = len(queue_node)
            print(queue_len)
            layer_queue = []
            layer_list = []
            for node in queue_node:
                if node:   
                    layer_list.append(node.val)  # 没有按照队列操作,每层输出
                    layer_queue.append(node.left)
                    layer_queue.append(node.right)
            if layer_list:
                node_list.append(layer_list)
            queue_node = layer_queue
        return node_list

方法2:  使用一个depth标记

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

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        # 层序遍历 按层访问
        # 队列  FIFO
        queue_node = [(root, 0)] # 深度
        cur_depth = 0  # 用来 确定是否换层
        ans = []
        temp = []
        for node, depth in queue_node:
            if node:
                # 判断 深度是否发生变化
                if cur_depth != depth:
                    cur_depth = depth
                    ans.append(temp)
                    temp = []
                temp.append(node.val)
                queue_node.append((node.left, depth+1))
                queue_node.append((node.right, depth+1)) # 子节点 所以深度+1
        if temp:
            ans.append(temp)
        return ans

 

2. 103. 二叉树的锯齿形层次遍历

给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

例如:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回锯齿形层次遍历如下:

[
  [3],
  [20,9],
  [15,7]
]

方法1:

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

class Solution:
    def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
        queue_node = [root]
        node_list = []
        flag = False # 为True 将每层node值结果倒序
        # 思路:保持层序遍历不变, 只在 需要从右到左时: 每层节点node值 倒序一下即可
        while queue_node:
            layer_list = []
            layer_node = []
            for node in queue_node:
                if node:
                    layer_list.append(node.val)
                    layer_node.append(node.left)
                    layer_node.append(node.right)
            if flag:
                layer_list.reverse()
            flag = not flag
            if layer_list:
                node_list.append(layer_list)
            queue_node = layer_node
        return node_list

3. 94. 二叉树的中序遍历

给定一个二叉树,返回它的中序 遍历。

示例:

输入: [1,null,2,3]
   1
    \
     2
    /
   3

输出: [1,3,2]

 方法1:  递归

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

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        # 1.递归  中序遍历
        if root:
            left = self.inorderTraversal(root.left)
            middle = left  +  [root.val]
            right = self.inorderTraversal(root.right)
            return middle + right
        return []

方法2: 迭代

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

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        # 2.迭代算法  
        value_list = []
        p = root  # p 当作移动指针
        stack = []  #栈:先进后出,  # 队列:先进先出;
        while p or stack:
            while p:  # 找左子树 一直找下去
                stack.append(p)
                p = p.left # p 移动 到 左孩子,直到为null
            # p 为null,从stack中pop
            p = stack.pop()
            value_list.append(p.val)
            # 处理右自子树
            p = p.right
        return value_list

4.  剑指 Offer 33. 二叉搜索树的后序遍历序列

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

参考以下这颗二叉搜索树:

     5
    / \
   2   6
  / \
 1   3

示例 1:

输入: [1,6,3,2,5]
输出: false

 示例 2:

输入: [1,3,2,6,5]
输出: true

解法1:

class Solution:
    def verifyPostorder(self, postorder: List[int]) -> bool:
        '''
        二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树:
         若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 
         若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 
         它的左、右子树 也分别为二叉排序树。
        '''
        '方法1: 递归'
        if len(postorder) <= 2:
            return True

        '后序遍历结果规律: 最后一个为根结点, 从左到右遍历,找到第一个大于 根结点的值(索引:k) '
        '左子树在:[0, k-1], 右子树在: [k, right-1], 即可递归'
        '判断条件:若满足左子树值都小于根结点, 右子树值都大于根节点, 为True'
        def verify(tree_list, left, right):
            # 递归必须 🈶️结束条件
            if left >= right:  
                return True 
            root = tree_list[right]
            # 寻找 第一个大于 root的 值
            k = left 
            while k < right and tree_list[k] < root:
                k = k + 1
            # 判断   
            index = k
            while index < right:
                if tree_list[index] < root:  # 如果右子树中 出现大于 根结点的 则 返回false
                    return False    
                index = index + 1
            if not verify(tree_list, left, k-1):  # 遍历左子树  
                return False
            if not verify(tree_list, k, right-1): # 遍历右子树
                return False

            return True  # 左右子树 都没有问题 才是 正确的

        return verify(postorder, 0, len(postorder)-1)

5. 合并二叉树

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

示例 1:

输入: 
	Tree 1                     Tree 2                  
          1                         2                             
         / \                       / \                            
        3   2                     1   3                        
       /                           \   \                      
      5                             4   7                  
输出: 
合并后的树:
	     3
	    / \
	   4   5
	  / \   \ 
	 5   4   7

解法1: 递归

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

class Solution:
    def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:
        new_tree = None
        if t1 and t2:  #  都不为null, 则 根节点相加
            new_tree = TreeNode(t1.val + t2.val)
            # 合并左子树
        if t1 and not t2:
            new_tree = t1
        if not t1 and t2:
            new_tree = t2
        if t1 and t2:  # 都不为null,才 继续遍历
            new_tree.left = self.mergeTrees(t1.left, t2.left)
            new_tree.right = self.mergeTrees(t1.right, t2.right)
        return new_tree

6. 从中序与后序遍历序列构造二叉树

根据一棵树的中序遍历与后序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]

返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

解法1:

# class TreeNode:  # 这一段必须注释掉,否则 输出一直为null
#     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:
            return None
        root_node = postorder[-1] # 根结点
        root_tree = TreeNode(root_node)
        index = inorder.index(root_node)
        zuo = inorder[:index]
        right = inorder[index+1:]
        zuo_post = postorder[:index]
        right_post = postorder[index:-1]
        if zuo:
            root_tree.left = self.buildTree(zuo, zuo_post)
        if right:
            root_tree.right = self.buildTree(right, right_post)
        return root_tree

7. 117. 填充每个节点的下一个右侧节点指针 II

给定一个二叉树

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

初始状态下,所有 next 指针都被设置为 NULL。

进阶:

你只能使用常量级额外空间。
使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。

示例:

输入:root = [1,2,3,4,5,null,7]
输出:[1,#,2,3,#,4,5,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。

解法1:

"""
# Definition for a Node.
class Node:
    def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next
"""

class Solution:
    def connect(self, root: 'Node') -> 'Node':
        # 层序遍历
        layer_node = [root]
        while layer_node:
            length = len(layer_node)
            nodes = []
            for i in range(0, length - 1):
                layer_node[i].next = layer_node[i+1]
                if layer_node[i].left:  # 每层的都添加进去
                    nodes.append(layer_node[i].left)
                if layer_node[i].right:
                    nodes.append(layer_node[i].right)
            # 切记 节点也可能为null 
            if layer_node[length-1] and layer_node[length-1].left:
                nodes.append(layer_node[length-1].left)
            if layer_node[length-1] and layer_node[length-1].right:
                nodes.append(layer_node[length-1].right)
            layer_node = nodes
        return root

8. 235. 二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉搜索树:  root = [6,2,8,0,4,7,9,null,null,3,5]

解法1:

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

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        # 二叉搜索树, 二叉排序树
        if not root:
            return None
        if p.val <= root.val and q.val >= root.val:
            return root
        if p.val >= root.val and q.val <= root.val:
            return root
        # 这样 两个 值 在 根结点的 同一侧
        # 同一侧 第一个相等的 肯定是 最近公共祖先。
        # 先找 左子树 
        node = self.lowestCommonAncestor(root.left, p, q)
        if not node:
            node = self.lowestCommonAncestor(root.right, p, q)
        
        return node

9. 113. 路径总和 II

给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。

说明: 叶子节点是指没有子节点的节点。

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

class Solution:
    def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
        # 必须是 从 根结点 到 叶子节点 的 路径总和
        all_node = []  # 设个全局变量
        node_list = []  # 栈  
        def bianli(root, sum, node_sum):  # 前序遍历
            if not root:
                return
            node_list.append(root.val)
            if not root.right and not root.left:
                # 必须是叶子节点, 必须结束了
                if sum ==  node_sum + root.val:
                    all_node.append(node_list.copy())  # 数组 必须copy一下, 不然全都改变了
                return  
            if root.left:
                bianli(root.left, sum, node_sum + root.val)
                node_list.pop() # 后进先出 # 回溯

            if root.right:
                bianli(root.right, sum, node_sum + root.val)
                node_list.pop() # 后进先出
            return 
        bianli(root, sum, 0)
        return all_node

10. 145. 二叉树的后序遍历

解法1: 递归解法

# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        
        if not root:  # 后序遍历
            return []  
        result = []
        if root.left:
            result += self.postorderTraversal(root.left)
        if root.right:
            result += self.postorderTraversal(root.right)
        result.append(root.val)
        return result

解法2: 非递归解法

11. 222. 完全二叉树的节点个数

给出一个完全二叉树,求出该树的节点个数。

说明:

完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

示例:

输入: 
    1
   / \
  2   3
 / \  /
4  5 6

输出: 6

解法1: O(N)

class Solution:
    def countNodes(self, root: TreeNode) -> int:
        if not root:
            return 0
        return self.countNodes(root.left) + self.countNodes(root.right) + 1

解法2: O(N)

class Solution:
    def countNodes(self, root: TreeNode) -> int:
        # 层序 全遍历一遍 节点
        nodes = [root]
        count_node = 0
        layer_node = []
        while nodes:
            for node in nodes:
                if node:
                    count_node = count_node + 1
                if node and node.left:
                    layer_node.append(node.left)
                    if node.right:
                        layer_node.append(node.right)
            nodes = layer_node
            layer_node = []
        return count_node

解法3: O(log(n)^2))

class Solution:
    def countNodes(self, root: TreeNode) -> int:
        if not root:
            return 0
        level = 0  # 计算有多少层
        node = root
        while node.left:   # 计算最深层, 
            level += 1
            node = node.left
        low = 1<<level  # 最后一层 上面的 所有节点数。 
        high = (1<<(level+1)) - 1  
        while low < high:  # 二分查找
            mid = (high - low + 1) // 2 + low  # 要确定是否存在的 节点
            if self.exists(root, level, mid):  # 判断节点是否存在
                low = mid
            else:
                high = mid - 1
        return low
    
    def exists(self, root, level, k):
        # 确定路径, 使用k的二进制确定
        k_2 = list(bin(k).split('b')[-1][1:])  #二进制 去除首位1, 剩余位为从根结点到节点的 路径, 1: 右子树,0:左子树
        k_2 = k_2[::-1]  # 
        node = root
        while k_2 and node: # 相比于node and k_2 可以节约运行时间
            if k_2.pop() == '1':
                node = node.right
            else:
                node = node.left
        return node != None
    # def exists(self, root, level, k):  # 看不太明白, 别人的 很正确的。
    #     bits = 1 << (level - 1)  # 上一层 
    #     node = root
    #     while node and bits > 0:  
    #         if bits & k == 0:  # 按路径查找 
    #             node = node.left
    #         else:
    #             node = node.right
    #         bits >>= 1  # 右移1位
    #     return node != None

12. 剑指 Offer 27. 二叉树的镜像  【面试遇到了】

请完成一个函数,输入一个二叉树,该函数输出它的镜像。

例如输入:

     4
   /   \
  2     7
 / \   / \
1   3 6   9

镜像输出:

     4
   /   \
  7     2
 / \   / \
9   6 3   1

限制:

0 <= 节点个数 <= 1000

解法1: 递归

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

class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        if not root:
            return None
        #temp = root.left
        #root.left = self.mirrorTree(root.right)
        #root.right = self.mirrorTree(temp)
        root.left, root.right = self.mirrorTree(root.right), self.mirrorTree(root.left)
        return root

解法2:  辅助栈或队列

class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        # 辅助栈或队列;
        zhan = [root]
        while zhan:
            node = zhan.pop()
            if not node:
                continue
            if node.left:
                zhan.append(node.left)
            if node.right:
                zhan.append(node.right)
            node.left, node.right = node.right, node.left
        return root

13. 236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉树:  root = [3,5,1,6,2,0,8,null,null,7,4]

示例 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。

示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

说明:

  • 所有节点的值都是唯一的。
  • p、q 为不同节点且均存在于给定的二叉树中。

解法1:   先记住, 要解释思路

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

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        # 先记住,脑子不行了
        if not root or root == p or root == q:
            return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if not left:
            return right
        if not right:
            return left
        
        return root  # 要先说 思路的 递归,

14. 98. 验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

输入:
    2
   / \
  1   3
输出: true

示例 2:

输入:
    5
   / \
  1   4
     / \
    3   6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
     根节点的值为 5 ,但是其右子节点值为 4 。

解法1:   二叉树 中序遍历 结果 是个 升序数组;

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        # 中序遍历结果是个 升序数组
        ans = []
        def zhong(root):
            if not root:
                return 
            zhong(root.left)
            ans.append(root.val)
            zhong(root.right)
        zhong(root)
        # 判断是否 升序排列
        # print(ans)
        first = ans[0]
        for i in range(1, len(ans)):
            if first < ans[i]:  # 大于 (不包括等于)
                first = ans[i]
            else:
                return False
        return True

解法2:    递归, 判断条件 

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        # 递归, 利用 左子树 小于根结点, 右子树大于根结点, 来判断 是否符合

        def valid(root, lower=float('-inf'), upper=float('inf')):
            if not root:  # 空节点为True; 
                return True
            # 不为bst则返回False, 为bst接着验证左右子树
            if not lower < root.val < upper:  # 判断依据
                return False
            if not valid(root.left, lower, root.val):
                return False
            if not valid(root.right, root.val, upper):
                return False
            return True  # 左右子树都为bst,则返回true
        
        return valid(root)  # 刚开始时,是无限小,无限大 [-inf, inf]

解法3: 前序 优化

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        global pre
        pre = float('-inf')  # pre 必须是全局才行
        def middleorder(root):
            if not root:
                return True
            if not middleorder(root.left):
                return False
            global pre  
            if root.val <= pre:  # 小于等于 false
                return False
            pre = root.val
            return middleorder(root.right)

        return middleorder(root)
            

15. 662. 二叉树最大宽度

给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空。

每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度

示例 1:

输入: 

           1
         /   \
        3     2
       / \     \  
      5   3     9 

输出: 4
解释: 最大值出现在树的第 3 层,宽度为 4 (5,3,null,9)。

解法1: 给每个节点 添加一个 深度标签 和 位置标签;

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def widthOfBinaryTree(self, root: TreeNode) -> int:
        # 宽度优先搜索; 给每个节点 一个 深度 和 pos ;
        queue = [(root, 0, 0)]
        cur_depth = left = ans = 0
        for node, depth, pos in queue:
            if node:
                queue.append((node.left, depth+1, pos*2))
                queue.append((node.right, depth+1, pos*2 + 1))
                if cur_depth != depth:  # 移动到下层了, 改变left 和深度
                    cur_depth = depth
                    left = pos
                ans = max(pos - left + 1, ans)
        return ans

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值