一、二叉树知识点概述

二叉树节点结构

class Node:
            def __init__(self, value, left=None, right=None, parent=None):
                self.value = value
                self.left = left
                self.right = right
                self.parent = parent # 非必须

搜索二叉树

一棵树中的所有子树中, 所有的左孩子都比父节点小, 所有的右孩子都比父节点大

完全二叉树

1 任意节点有右孩子没有左孩子  return false
        2 在不违背第一条件的情况下, 如果遇到左右孩子不全的情况下, 在加下来遇到的所有孩子都必须为 叶子节点

平衡二叉树

每一科子树的左树和右树的高度差不超过1

满二叉树

满足 结点数量为 2^树的高度 - 1 的二叉树

二、树, 队列, 堆栈的简单数据结构

class Node:def __init__(self, value, left=None, right=None):self.value = valueself.left = leftself.right = rightdef add_left(self, l_node):self.left = l_nodedef add_right(self, r_node):self.right = r_nodedef change_value(self, val):self.value = valclass Stack:def __init__(self):self.elems = []def push(self, item):self.elems.append(item)def pop(self):if not self.elems:raise "堆栈已经空了"return self.elems.pop()def is_empty(self):return self.elems == []def top(self):return self.elems[-1]def size(self):return len(self.elems)class Queue:def __init__(self):self.elems = []def pop(self):if self.elems:return self.elems.pop(0)else:raise "队列已经空了"def push(self, item):self.elems.append(item)def size(self):return len(self.elems)def is_empty(self):return self.elems == []def top(self):return self.elems[0]

三、二叉树问题汇总

class BinaryTreeQues:def __init__(self, tree=None):self.tree = treedef pre(self):"""前序遍历 根左右:return:"""s = Stack()s.push(self.tree)pre_arr = []while s.size():pre_arr.append(s.pop())print(pre_arr[-1].value, end=" ")if pre_arr[-1].left:s.push(pre_arr[-1].left)if pre_arr[-1].right:s.push(pre_arr[-1].right)print()return pre_arrdef post(self):"""后序遍历 左右根:return:"""# 注释代码为用堆栈存储遍历序列post_arr = []s1 = Stack()# s2 = Stack()s1.push(self.tree)while s1.size():# item = s1.pop()# s2.push(item)post_arr.append(s1.pop())if post_arr[-1].right:s1.push(post_arr[-1].right)if post_arr[-1].left:s1.push(post_arr[-1].left)# while s2.size():#     print(s2.pop().value, end=" ")# print()return list(reversed(post_arr))def mid(self):"""中序遍历 左根右:return:"""mid_arr = []def push_all_left(tree_node, stack: Stack):"""将当前根节点的所有左孩子节点放入堆栈:param tree_node: 子树根节点:param stack: 辅助堆栈:return:"""while tree_node:stack.push(tree_node)tree_node = tree_node.lefts = Stack()  # 初始化一个堆栈push_all_left(self.tree, s)  # 将树的根节点的所有左孩子放入堆栈while s.size():mid_arr.append(s.pop())  # 在堆栈中拿出栈顶节点print(mid_arr[-1].value, end=" ")if mid_arr[-1].right:  # 如果有右孩子 则将右孩子节点为子树的根节点 并将该子树中的所有左孩子节点放入堆栈(包括自身)push_all_left(mid_arr[-1].right, s)print()return mid_arr@staticmethoddef mid_by_left(head):"""左神写的中序遍历算法Java代码转翻:param head: self.tree:return:"""if head:s = Stack()while s.size() or head:if head:s.push(head)head = head.leftelse:head = s.pop()print(head.value, end=" ")head = head.rightprint()returndef DFS(self):"""宽度优先遍历层序遍历:return:"""if not self.tree:returnq = Queue()q.push(self.tree)while not q.is_empty():item = q.pop()print(item.value, end=" ")if item.left:q.push(item.left)if item.right:q.push(item.right)print()returndef DFS_with_level(self):"""获取每层都有哪些节点, 和拥有最大节点数量的层的节点数量:return: 树每层的节点值, 树中有最大节点数量的层的节点数量"""if not self.tree:  # 如果树不存在 则返回returnnodes_dic = {}  # 纪录每层的节点值(顺序为由左至右)cur_level = level = 0  # 当前层数, 所在层数q = Queue()  # 辅助队列max_nodes_index = -1  # 拥有最多节点的层数的节点数量cur_nodes_index = 0  # 当前层的节点数量(暂时量)q.push((self.tree, level))  # 将根节点放入辅助队列while not q.is_empty():item, level = q.pop()  # 弹出节点, 节点所在层'''''''''区域内代码为计算拥有最大节点数量的层的节点数量, 如若不需要可以删除'''if level == cur_level:  # 如果当前层和所在层一致cur_nodes_index += 1  # 当前层节点 +1else:cur_level = level  # 将当前层置换为所在层cur_nodes_index = 1  # 当前节点数量 置为 1if cur_nodes_index > max_nodes_index:  # 判断当前层节点数量是否大于了纪录的最大节点数max_nodes_index = cur_nodes_index''''''if level in nodes_dic:  # 是否为该层的第一个节点nodes_dic[level].append(item.value)  # 如果不是 直接扩充else:nodes_dic[level] = [item.value]  # 如果是 新建列表并将当前节点添加到列表中if item.left:  # 如果有左孩子, 则将左孩子放入队列q.push((item.left, level + 1))if item.right:  # 如果有右孩子, 则将右孩子放入队列q.push((item.right, level + 1))return nodes_dic, max_nodes_indexdef DFS_get_max_nodes(self):"""拥有最大节点数量的层的节点数量:return: 拥有最大节点数量的层的节点数量"""cur_level = level = 0q = Queue()max_nodes = 0cur_node = 0q.push((self.tree, level))while not q.is_empty():item, level = q.pop()if cur_level != level:cur_level = levelcur_node = 1else:cur_node += 1max_nodes = cur_node if cur_node > max_nodes else max_nodesif item.left:q.push((item.left, level + 1))if item.right:q.push((item.right, level + 1))return max_nodes@staticmethoddef DFS_get_max_nodes_by_left(head):"""左神 Java代码转翻拥有最大节点数量的层的节点数量:return: 拥有最大节点数量的层的节点数量"""if not head:returnq = Queue()cur_end = head  # 当前层数最后一个节点next_end = None  # 下一层最后一个节点cur_nodes_index = 0  # 当前层的节点数量max_nodes_index = 0  # 拥有最多节点的层的节点数量q.push(head)  # 放入根节点while not q.is_empty():item = q.pop()  # 拿出队列中的第一个节点cur_nodes_index += 1  # 当前层节点数量 + 1if item.left:  # 如果有左孩子q.push(item.left)  # 将左孩子放入队列next_end = item.left  # 下一层的最后一个节点置为放入队列中的最后一个节点if item.right:  # 同理q.push(item.right)next_end = item.rightif item == cur_end:  # 如果当前节点为当前层的最后一个节点# 比较当前层的节点数量是否比以往层的最大节点数量多 如果多的话则给 max_nodes 赋值max_nodes_index = cur_nodes_index if max_nodes_index < cur_nodes_index else max_nodes_indexcur_nodes_index = 0  # 当前层的节点数量置零cur_end = next_end  # 当前层的最后节点为下一层的最后一个节点return max_nodes_indexdef isBST(self):"""Binary Search Tree是否为搜索二叉树方法: 中序遍历, 将原本的打印操作变为比较操作:return:"""min_value = -1def push_all_left(tree_node, stack: Stack):while tree_node:stack.push(tree_node)tree_node = tree_node.lefts = Stack()  # 初始化一个堆栈push_all_left(self.tree, s)  # 将树的根节点的所有左孩子放入堆栈while s.size():item = s.pop()  # 在堆栈中拿出栈顶节点'''核心代码'''if item.value <= min_value:return Falseelse:min_value = item.value'''核心代码结束'''if item.right:  # 如果有右孩子 则将右孩子节点为子树的根节点 并将该子树中的所有左孩子节点放入堆栈(包括自身)push_all_left(item.right, s)return Truedef isCBT(self):"""Complete Binary Tree是否为完全二叉树方法: 层序遍历1 任意节点有右孩子没有左孩子  return false2 在不违背第一条件的情况下, 如果遇到左右孩子不全的情况下,在加下来遇到的所有孩子都必须为 叶子节点:return:"""if not self.tree:return Trueq = Queue()q.push(self.tree)flag = 0  # 0: 没有左右孩子 1: 有左孩子 2: 有右孩子 3: 有左右孩子 -1: 从此节点开始之后应全为叶子节点while not q.is_empty():item = q.pop()if item.left:if flag == -1:return Falseflag += 1q.push(item.left)if item.right:if flag <= 0:  # 0 or -1return Falseflag += 2q.push(item.right)print(f'({flag}, {item.value})', end=" ")if -1 <= flag <= 1:  # -1 or 0 or 1flag = -1elif flag:flag = 0return True'''二叉树解题套路(递归方法)(树形DP)1 分解子问题(搜寻条件)2 问询左右子树信息, 将所有可能性列出3 根据获取到的信息 做判断举例(是否我平衡二叉树)1 左子树是否为平衡二叉树, 右子树是否为平衡二叉树左侧的value_max < cur_node.value右侧的value_min > cur_node.value2 得到信息3 判断 左子树为平衡二叉树右子树是平衡二叉树左右子树加上父节点是否为平衡二叉树如果上述全部 满足, 则为平衡二叉树'''def is_BST_by_recursion(self):"""是否为搜索二叉树 ( 递归解法 ):return:"""def process(node: Node):if not node:return True, None, Nonel_is_bst, l_min, l_max = process(node.left)r_is_bst, r_min, r_max = process(node.right)is_bst = Truemax_ = min_ = node.valueif l_min:min_ = min(min_, l_min)if r_min:max_ = max(max_, r_max)# 左子树存在, 左子树不是搜索二叉树或者 左侧的最大值大于等于了当前节点的值# 右子树存在, 右子树不是搜索二叉树或者 左侧的最小值小于等于了当前节点的值if (l_min and (not l_is_bst or l_max >= node.value)) \or (r_min and (not r_is_bst or r_min <= node.value)):is_bst = False  # 该树不是搜索二叉树return is_bst, min_, max_  # 返回结果return process(self.tree)[0]def is_balanced_by_recursion(self):def process(node: Node):if not node:return True, 0l_height, l_is_balanced = process(node.left)  # 问询左子树是否为平衡二叉树r_height, r_is_balanced = process(node.right)  # 问询右子树是否为平衡二叉树height = max(l_height, r_height) + 1  # 获取当前节点发散出去的树的高度# 判断当前树是否为平衡二叉树#               左树              右树                 拼接后的树is_balanced = l_is_balanced and r_is_balanced and abs(l_height - r_height) < 2# 返回结果和当前树的高度return height, is_balancedreturn process(self.tree)[0]def is_FBT_by_recursion(self):"""Full Binary Tree是否为满二叉树 ( 递归解法 ):return:"""def process(node: Node):if not node:return 0, 0l_height, l_nodes_index = process(node.left)r_height, r_nodes_index = process(node.right)inner_height = max(l_height, r_height) + 1inner_nodes_index = l_nodes_index + r_nodes_index + 1return inner_height, inner_nodes_indexheight, node_index = process(self.tree)# 判断根据, 节点数量 等于 2^树的高度 - 1return node_index == (1 << height) - 1def lowest_common_ancestor(self, node_1, node_2):'''找到 node_1 和 node_2 的共同祖先:param node_1::param node_2::return: 树的学院关系表, node_1 和 node_2 的共同祖先'''ancestor_dic = {self.tree: [self.tree]}def generate_relational_table(node):if not node:  # 如果节点不存在返回returnif node.left:  # 如果存在左孩子ancestor_dic[node.left] = ancestor_dic.get(node, []).copy()  # 复制父节点的血缘关系ancestor_dic[node.left].append(node.left)  # 添加本节点generate_relational_table(node.left)  # 向下获取血缘关系if node.right:ancestor_dic[node.right] = ancestor_dic.get(node, []).copy()ancestor_dic[node.right].append(node.right)generate_relational_table(node.right)generate_relational_table(self.tree)  # 生成整棵树的血缘关系def check_in():flag = Noneprint("-----------------")for i in reversed(ancestor_dic.get(node_1)):  # 从下往上查血缘关系print(i.value)if i in ancestor_dic.get(node_2):  # 如果查找到的血缘关系在node_2的血缘关系表中flag = i  # 获取共同祖先节点breakprint("-----------------")return flag  # 返回return ancestor_dic, check_in()def lowest_common_ancestor_by_left(self, cur_node, node_1, node_2):'''左神 Java 代码转翻1 node_1 是 node_2 的祖先 或者反过来2 node_1 和 node_2 是通过汇聚找到一个共同祖先:param cur_node: 当前节点:param node_1: 要寻找共同祖先节点1:param node_2: 要寻找共同祖先节点2:return:    1: 空, node_1, node_22: 共同祖先(如果不是根节点 会直接向上传递 直到最后返回)3: 左节点为 None 返回右节点 否则返回左节点'''# 当前节点为空, 当前节点为节点1 或者当前节点为节点2if not cur_node or cur_node == node_1 or cur_node == node_2:return cur_node  # 1left_node = self.lowest_common_ancestor_by_left(cur_node.left, node_1, node_2)right_node = self.lowest_common_ancestor_by_left(cur_node.rightm, node_1, node_2)# 如果左右节点都不为空if left_node and right_node:# 则表示当前节点为 node_1, node_2的共同祖先return cur_node  # 2return left_node if left_node else right_node  # 3@staticmethoddef find_successor_node(node):"""找 node 的后继节点在二叉树的中序遍历中, node的下一个节点叫 node 的后继节点:param node: 带有父节点指针的节点 且假定整棵树节点都有父节点的指针:return: node的后继节点, 如果没有 返回 None"""if not node:returnif rn := node.right:# 如果该节点有右子树, 则右子树的最左节点为 node 的后继节点while rn.left: rn = rn.leftreturn rnelse:parent = node.parentwhile parent and parent.right is node:'''如果父节点不为 None 且该节点为父节点的右孩子向上查找一直查找到某一层为该层父节点的左孩子则此时的父节点即为 node 的后继节点如果最后找到父节点为 None 即找到了根节点 则说明该节点为此树的最后一个节点 无后继节点 返回 None'''node = parentparent = node.parentreturn parentdef Serialization(self):"""序列化:return:"""if not self.tree:returnq = Queue()q.push(self.tree)dic = {self.tree.value: None}while not q.is_empty():item = q.pop()if item.left:dic[item.left.value] = f'{item.value}_l'q.push(item.left)if item.right:dic[item.right.value] = f'{item.value}_r'with open("test.txt", "w", encoding="utf8") as f:json.dump(dic, f)def deserialization(self, ser_dic):'''反序列化:param ser_dic:存储树结构的字典:return:'''node_list = {}for key, val in ser_dic.items():node_list[key] = Node(key)if val:value, loc = val.split("_")if loc == "l":node_list[value].add_left(node_list[key])else:node_list[value].add_right(node_list[key])for key in ser_dic:self.tree = node_list[key]return self.treedef origami_problem(self, n):"""折纸问题:param n: 折了几次:return:"""if n < 1:returnroot_node = Node(0)n_level_nodes = [root_node]last_level_nodes = []for i in range(1, n + 1):for node in last_level_nodes:node.add_left(Node(0))node.add_right(Node(1))n_level_nodes.extend([node.left, node.right])last_level_nodes = n_level_nodes.copy()n_level_nodes.clear()self.tree = root_nodereturn self.mid()

四、自定义打印函数

def print_nodes_value(nodes_arr):'''根据遍历序列输出:param nodes_arr::return:'''if not nodes_arr:returnfor i in nodes_arr:print(i.value, end=" ")print()def print_nodes_in_dic_value(nodes_dic):for key, value in nodes_dic.items():print(key.value, end=" ")for val in value:print(val.value, end=" ")print()print()

五、主函数

if __name__ == '__main__':# tree_10 = Node(10)# tree_9 = Node(9)# tree_8 = Node(8)# tree_6 = Node(6, )# tree_1 = Node(1, )# tree_4 = Node(4, )# tree_7 = Node(7, tree_6, tree_8)# tree_2 = Node(2, tree_1)# tree_3 = Node(3, tree_2, tree_4)# tree_5 = Node(5, tree_3, tree_7)# b = BinaryTreeQues(tree_5)# # 层序遍历# b.DFS_with_level()# # 获取拥有最多节点的层的节点数量# res = b.DFS_get_max_nodes()# res = b.DFS_get_max_nodes_by_left()# # 中序遍历# res = b.mid()# print_nodes_value(res)# # 是否为搜索二叉树# res = b.isBST_by_recursion()# # 是否为完全二叉树# res = b.isCBT()# # 是否为满二叉树# res = b.is_FBT_by_recursion()# print(res)# # 查找共同祖先# res, check_res = b.lowest_common_ancestor(tree_1, tree_4)# print_nodes_in_dic_value(res)# print(check_res.value)# # 序列化和反序列化# b.Serialization()# with open("test.txt", "r", encoding="utf8") as f:#     dic = json.load(f)b = BinaryTreeQues()# res = b.deserialization(dic)# b.DFS()res = b.origami_problem(3)print_nodes_value(res)

本文根据 左神课程编写(一周刷爆LeetCode,算法大神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解_哔哩哔哩_bilibili)

其中代码部分 除标_by_left 其余均为博主自己编写(可能有些跟视频中的算法相重合)

[算法入土之路]二叉树相关推荐

  1. 新手算法学习之路----二叉树(二叉树最大路径和)

    摘抄自:https://segmentfault.com/a/1190000003554858#articleHeader2 题目: Given a binary tree, find the max ...

  2. 拿下斯坦福和剑桥双offer,00后的算法学习之路

    董文馨,00后,精通英语,西班牙语.斯坦福大学计算机系和剑桥大学双Offer,秋季将进入斯坦福大学学习. 10岁开始在国外上学:12岁学Scratch: 13岁学HTML & CSS: 14岁 ...

  3. Algorithm:【Algorithm算法进阶之路】之数据结构二十多种算法演示

    Algorithm:[Algorithm算法进阶之路]之数据结构二十多种算法演示 目录 一.数据结构算法 1.顺序表 2.链表 3.栈和队列 4.串的模式匹配 5.稀疏矩阵 6.广义表 7.二叉树 8 ...

  4. Algorithm:【Algorithm算法进阶之路】之十大经典排序算法

    Algorithm:[Algorithm算法进阶之路]之十大经典排序算法 相关文章 Algorithm:[Algorithm算法进阶之路]之数据结构二十多种算法演示 Algorithm:[Algori ...

  5. Algorithm:【Algorithm算法进阶之路】之算法中的数学编程相关习题(时间速度、进制转换、排列组合、条件概率、斐波那契数列)

    Algorithm:[Algorithm算法进阶之路]之算法中的数学编程相关习题(时间速度.进制转换.排列组合.条件概率.斐波那契数列) 目录 时间速度 排列组合 进制转换 条件概率 斐波那契数列 时 ...

  6. Algorithm:【Algorithm算法进阶之路】之数据结构基础知识

    Algorithm:[Algorithm算法进阶之路]之数据结构基础知识 相关文章 Algorithm:[Algorithm算法进阶之路]之数据结构二十多种算法演示 Algorithm:[Algori ...

  7. 【算法学习笔记】二叉树的基本操作实现和应用举例,根据先序与中序遍历建立二叉树的实现

    基本操作 给某结点插入一个左孩子 删除二叉树中某结点的左子树 根据先序与中序遍历建立二叉树 应用举例 查找数据元素 二叉链表存储结构 统计叶结点的数目 基本操作 通常有:建立空二叉树.生成以X为根节点 ...

  8. 【实践】汽车之家推荐系统排序算法迭代之路.pdf(附下载链接)

    今天给大家分享汽车之家李晨旭先生所做的分享<汽车之家推荐系统排序算法迭代之路.pdf>. 本PPT已收录到小程序省时查报告中,大家可以到省时查报告小程序中查看并下载文档全文. 更多细节和相 ...

  9. 《数据结构与算法》——树与二叉树之遍历总结

    <数据结构与算法>--树与二叉树之遍历总结 树与二叉树部分计划分为三次进行复习总结,第一次为基本概念和二叉树的遍历,第二次内容为线索二叉树以及树和森林,第三次为树与二叉树的应用. 目录 & ...

最新文章

  1. [翻译]Joomla 1.5架构(十一) model 包
  2. 某大学园区网规划(设想)
  3. Android驱动开发读书笔记六
  4. ubuntu更新rtl8192cu驱动
  5. Spring Cloud学习笔记-010
  6. 阿里研究员谷朴:API 设计最佳实践的思考
  7. 零基础 ABAP 学习教程系列文章的目录
  8. JavaFX UI控件教程(三)之Label
  9. 微服务有麻烦吗? Lagom在这里为您提供帮助。 尝试一下!
  10. 边工作边刷题:70天一遍leetcode: day 56-1
  11. linux脚本编写乘法口诀,shell脚本编写乘法口诀
  12. 关于解决Win10家庭中文版没有组策略编辑器的问题
  13. 刷脸支付会逐步取代扫码成为主流支付方式
  14. Matlab画图线型、符号及颜色设置
  15. PC机并口、并口扩展卡使用EPP模式通信,ECP SPP等
  16. DongDong认亲戚 来源:牛客网
  17. 学习笔记4--自动驾驶汽车感知系统
  18. 使用python切分mp4视频并保存每帧图像
  19. 汽车颗粒物排放对环境和健康的影响及政府监管策略
  20. 新教育杂志新教育杂志社新教育编辑部2023年第6期目录

热门文章

  1. 房子室内应该怎么画?如何画好窗帘和窗户?
  2. python包和模块推荐_对于Python包和模块的10个常识清单
  3. 抖音 Android 性能优化系列:Java 内存优化篇
  4. C.H. Robinson罗宾逊全球物流推出跨欧亚铁路货运服务
  5. 万向区块链技术研究报告 | EOS.IO技术调研(二)
  6. 离散数学/初等数论:用“质因子分解法”和“欧几里得算法”求最大公约数gcd;“质因子分解法”和“最简算法”求最小公倍数lcm;以及对“意义”的一些看法。
  7. 如何用计算机名安装打印机,打印机如何安装驱动 电脑如何连接打印机
  8. A002-186-2623-余友龙
  9. Angular ngIf相关问题
  10. 如何年赚百万,2021发财指南