数据结构与算法python—9.二叉树及python实现
文章目录
- 引言
- 一、树基础
- 1.树的种类
- 2.树的存储与表示
- 二、二叉树
- 1.二叉树的概念
- 2.完全二叉树与满二叉树
- 3.二叉树的性质
- 4.二叉树的存储方式
- 三、二叉树的基本操作
- 1.树的创建—向二叉树插入节点
- 2.二叉树的遍历
- 2.1 深度优先遍历
- 2.1.1 前序遍历
- 2.1.2 中序遍历
- 2.1.3 后序遍历
- 2.1.4 总结
- 2.2 广度优先遍历
- 2.3 全部代码
- 3.推导遍历结果
引言
树可以像有序数组那样快速查找数据,也可以像链表那样快速插入数据。
一、树基础
树是一种抽象数据类型(ADT)。它是由n(n>=1)个有限节点组成一个具有层次关系的集合。树主要有如下特征:
- 每个节点有零个或多个子节点
- 没有父节点的节点称为根节点;
- 每一个非根节点有且只有一个父节点;
- 除了根节点外,每个子节点可以分为多个不相交的子树
如果一个子节点有多个父节点,那么子树相交
下面展示一些树的术语:
- 节点的度:一个节点含有的子树的个数称为该节点的度;
- 树的度:一棵树中,最大的节点的度称为树的度;
- 叶子节点或终端节点:度为零的节点;
- 父亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
- 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
- 兄弟节点:具有相同父节点的节点互称为兄弟节点;
- 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
- 树的高度或深度:树中节点的最大层次;
- 堂兄弟节点:父节点在同一层的节点互为堂兄弟;
- 节点的祖先:从根到该节点所经分支上的所有节点;
- 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
- 森林:由m(m>=0)棵互不相交的树的集合称为森林;
总结:根节点没有父亲,叶结点没有孩子,中间节点可以有一个父亲多个孩子
1.树的种类
- 有序树与无序树:如果将树中结点的各子树看成从左至右是有次序的(即不能互换),则称该树为有序树,否则称为无序树。
- 完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树,其中满二叉树的定义是所有叶节点都在最底层的完全二叉树
- 平衡二叉树(AVL树):当且仅当任何节点的两棵子树的高度差不大于1的二叉树;
- 排序二叉树(二叉查找树(英语:Binary Search Tree),也称二叉搜索树、有序二叉树);
- 霍夫曼树(用于信息编码):带权路径最短的二叉树称为哈夫曼树或最优二叉树;
- B树:一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多余两个子树。
2.树的存储与表示
- 顺序存储:将数据结构存储在固定的数组中,在遍历速度上有一定的优势,但因所占空间比较大,是非主流二叉树。二叉树通常以链式存储。
- 链式存储:可以存储,缺点是指针域指针个数不定
二、二叉树
1.二叉树的概念
二叉树是n个结点的有限集合,该集合或者为空集,或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。二叉树天然具有递归结构,二叉树中包含二叉树。
二叉树(Binary Tree)是一种特殊的树型结构,它的特点是每个结点至多有两棵子树,且二叉树的子树有左右之分,其次序不能任意颠倒。即使树中只有一颗子树,也要区分左子树与右子树。
二叉树有五种基本形态:
- 空二叉树
- 只有一个根节点
- 根结点只有左子树
- 根结点只有右子树
- 根结点既有左子树又有右子树
只有3个节点时,树有如下形态
2.完全二叉树与满二叉树
完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树,其中满二叉树的定义是所有叶节点都在最底层的完全二叉树。
满二叉树举例:
完全二叉树举例:
满二叉树一定是完全二叉树,但是完全二叉树并不一定是满二叉树。两者的区别在于树的最后一层满没满。并且还要关注第d层所有节点是否从左向右连续地紧密排列,如果没有连完全二叉树都不是。
对一棵具有n个结点的二叉树按层序编号,如果编号为i的结点与同样深度的满二叉树中编号结点为i的结点在二叉树中位置完全相同,则这棵二叉树成为完全二叉树
下面总结完全二叉树的特点:
- 叶子结点只能出现在最下两层
- 最下层的叶子一定集中在左部连续位置
- 倒数二层,若有叶子结点,一定都在右部连续位置
- 如果结点度为1,则该结点只有左孩子,即不存在只有右子树的情况
- 同样结点数的二叉树,完全二叉树的深度最小
3.二叉树的性质
- 在二叉树的第iii层上至多有2i−12^{i-1}2i−1个结点(i>0)(i>0)(i>0)
- 深度为kkk的二叉树至多有2k−12^k - 12k−1个结点(k>0)(k>0)(k>0)
- 对于任意一棵二叉树TTT,如果其叶节点数为N0N_0N0,而度数为2的结点总数为N2N_2N2,则N0=N2+1N_0=N_2+1N0=N2+1;
- 具有n个结点的完全二叉树的深度必为 [log2n]+1[log_2n]+1[log2n]+1
[x]表示不大于x的最大整数
- 对完全二叉树,若从上至下、从左至右编号,则编号为iii 的结点,其左孩子编号必为2i2i2i,其右孩子编号必为2i+12i+12i+1;其双亲的编号必为i//2i//2i//2(i=1 时为根,除外)
4.二叉树的存储方式
二叉树可以链式存储,也可以顺序存储。下面展示链式存储:
下面展示在顺序存储:
在数组中,父节点编号为iii,则左孩子的编号为2i+12i+12i+1,右孩子的编号为2i+22i+22i+2。
三、二叉树的基本操作
1.树的创建—向二叉树插入节点
插入一个元素,逐层向下插入
# 二叉树的创建
class Tree(object):def __init__(self):# 定义根节点,永不删除,作为哨兵使用self.root = Node('root')# 向二叉树插入节点def add(self, item):node = Node(item)# 虽然没有起到作用,但这是边界的必要检查if self.root == None:self.root = nodeelse:# 设置一个队列q = [self.root]# 逻辑是先判断有没有左孩子与右孩子,没有的话赋值,有的话添加到队列中while True:pop_node = q.pop(0)if pop_node.left is None:pop_node.left = nodereturnelif pop_node.right is None:pop_node.right = nodereturnelse:q.append(pop_node.left)q.append(pop_node.right)
2.二叉树的遍历
二叉树的遍历,指的是如何按某种搜索路径遍历树中的每个结点,使得每个结点均被访问一次,而且仅被访问一次。
二叉树主要有两种遍历方式:
- 深度优先遍历:先往深走,遇到叶子节点再往回走。
- 广度优先遍历:一层一层的去遍历。
2.1 深度优先遍历
2.1.1 前序遍历
前序遍历操作定义为:若二叉树为空,为空操作;否则根 -> 左 -> 右
- 访问根节点;
- 先序遍历左子树;
- 先序遍历右子树。
# 前序遍历
def preorder(self, root):# 边界if root is None:return []result = [root.item]# 递归调用left_item = self.preorder(root.left)right_item = self.preorder(root.right)return result + left_item + right_item
2.1.2 中序遍历
中序遍历操作定义为:若二叉树为空,为空操作;否则左 -> 根 -> 右
- 中序遍历左子树;
- 访问根结点;
- 中序遍历右子树。
# 中序遍历
def inorder(self,root):if root is None:return []result = [root.item]# 递归调用left_item = self.inorder(root.left)right_item = self.inorder(root.right)return left_item + result + right_item
2.1.3 后序遍历
后序遍历操作定义为:若二叉树为空,为空操作;否则左 -> 右 -> 根
- 后序遍历左子树;
- 后序遍历右子树;
- 访问根结点。
# 后序遍历
def postorder(self,root):if root is None:return []result = [root.item]# 递归调用left_item = self.postorder(root.left)right_item = self.postorder(root.right)return left_item + right_item + result
2.1.4 总结
这里的前中后指的是根节点的位置。
2.2 广度优先遍历
广度优先遍历也叫作层序遍历。操作定义为:若二叉树为空,为空操作;否则从上到下、从左到右按层次进行访问。
# 广度优先遍历/层次遍历
def traverse(self):if self.root is None:return None# 定义一个队列来存放二叉树q = [self.root]res = [self.root.item]while q != []:pop_node = q.pop(0)if pop_node.left != None:q.append(pop_node.left)res.append(pop_node.left.item)if pop_node.right != None:q.append(pop_node.right)res.append(pop_node.right.item)return res
2.3 全部代码
"""
使用python实现链式存储的二叉树
"""
# 定义节点
class Node(object):def __init__(self, item):self.item = itemself.left = Noneself.right = Nonedef __str__(self):return str(self.item)# 定义二叉树
class Tree(object):def __init__(self):# 定义根节点self.root = None# 定义根节点,永不删除,作为哨兵使用# self.root = Node('root')# 向二叉树插入节点def add(self, item):node = Node(item)# 虽然没有起到作用,但这是边界的必要检查if self.root == None:self.root = nodeelse:# 设置一个队列q = [self.root]# 逻辑是先判断有没有左孩子与右孩子,没有的话赋值,有的话添加到队列中while True:pop_node = q.pop(0)if pop_node.left is None:pop_node.left = nodereturnelif pop_node.right is None:pop_node.right = nodereturnelse:q.append(pop_node.left)q.append(pop_node.right)# # 函数追踪结果比debug更快# def track(func):# @functools.wraps(func)# def inner(*args):# result = func(*args)# print("{} --> ({}) --> {} ".format(func.__name__, args[0], result))# return result## return inner# 前序遍历# @functools.lru_cache()# @trackdef preorder(self, root):# 边界if root is None:return []result = [root.item]# 递归调用left_item = self.preorder(root.left)right_item = self.preorder(root.right)return result + left_item + right_item# 中序遍历def inorder(self, root):if root is None:return []result = [root.item]# 递归调用left_item = self.inorder(root.left)right_item = self.inorder(root.right)return left_item + result + right_item# 后序遍历def postorder(self, root):if root is None:return []result = [root.item]# 递归调用left_item = self.postorder(root.left)right_item = self.postorder(root.right)return left_item + right_item + result# 广度优先遍历/层次遍历def traverse(self):if self.root is None:return None# 定义一个队列来存放二叉树q = [self.root]res = [self.root.item]while q != []:pop_node = q.pop(0)if pop_node.left != None:q.append(pop_node.left)res.append(pop_node.left.item)if pop_node.right != None:q.append(pop_node.right)res.append(pop_node.right.item)return resif __name__ == '__main__':t = Tree()for i in range(10):t.add(i)print('层次遍历', t.traverse())print('先序遍历', t.preorder(t.root))print('中序遍历', t.inorder(t.root))print('后序遍历', t.postorder(t.root))
层次遍历 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
先序遍历 [0, 1, 3, 7, 8, 4, 9, 2, 5, 6]
中序遍历 [7, 3, 8, 1, 9, 4, 0, 5, 2, 6]
后序遍历 [7, 8, 3, 9, 4, 1, 5, 6, 2, 0]
3.推导遍历结果
- 已知前序遍历和中序遍历序列,可以唯一确定一棵二叉树
- 已知后序遍历和中序遍历序列,可以唯一确定一棵二叉树
已知一棵二叉树的前序遍历序列为ABCDEF,中序遍历序列为CBAEDF,请问这颗二叉树的后序遍历结果是多少?
C -> B -> E -> F -> D -> A
二叉树的中序序列是ABCDEFG,后序序列是BDCAFGE,求前序序列
E -> A -> C -> B -> D -> G -> F
先序+中序,后序+中序都可以确定所有父节点
如果对您有帮助,麻烦点赞关注,这真的对我很重要!!!如果需要互关,请评论或者私信!
数据结构与算法python—9.二叉树及python实现相关推荐
- 视频教程-Python数据结构与算法面试(上)-Python
Python数据结构与算法面试(上) 东北大学计算机专业硕士研究生,欧瑞科技创始人&CEO,曾任国内著名软件公司项目经理,畅销书作者,企业IT内训讲师,CSDN学院专家讲师,制作视频课程超过1 ...
- Python数据结构与算法(1.5)——Python基础之函数与异常
Python数据结构与算法(1.5)--Python基础之函数与异常 0. 学习目标 1. 函数 1.1 自定义函数 1.2 函数与参数 1.3 函数与返回值 2. 异常处理 2.1 raise 语句 ...
- Python数据结构与算法(1.4)——Python基础之控制结构
Python数据结构与算法(1.4)--Python基础之控制结构 0. 学习目标 1. 代码块与缩进 2. 条件语句 2.1 if 语句 2.2 if 语句的嵌套 2.3 断言 3. 循环 3.1 ...
- Python数据结构与算法(1.3)——Python基础之输入、输出与高阶赋值
Python数据结构与算法(1.3)--Python基础之输入.输出与高阶赋值 0. 学习目标 1. 输入.输出与注释 1.1 获取用户输入 1.2 格式化输出 1.2.1 基本方法 1.2.2 fo ...
- Python数据结构与算法(1.2)——Python基础之变量与内置数据类型
Python数据结构与算法(1.2)--Python基础之变量与内置数据类型 0. 学习目标 1. Python 程序的运行 1.1 Python 交互式解释器 1.2 Python 程序脚本 2. ...
- Python数据结构与算法(1.6)——Python基础之类与模块化
Python数据结构与算法(1.6)--Python基础之类与模块化 0. 学习目标 1. 面向对象编程:类 1.1 面向对象编程的基本概念 1.1.1 多态 1.1.2 封装 1.1.3 继承 1. ...
- 数据结构与算法--死磕二叉树
死磕二叉树 近一年都比较关注算法相关的知识,也刷了不少题,之前的文章中大多也是算法相关的文章,但是感觉每次遇到树相关的题型都不能应对自如,因此还是有必要在相关知识上下功夫,因此有此次总结,以下是所有树 ...
- apriori算法c++实现_经典数据结构与算法(四):Python/C/C ++实现队列类型双端队列数据结构...
前期文章点击这里: 经典数据结构与算法(一):Python/C/C ++实现堆栈和队列 双端队列或双端队列是一种队列,其中可以从前面或后面执行元素的插入和删除.因此,它不遵循FIFO规则(先进先出). ...
- c++ 队列_经典数据结构与算法(一):Python/C/C ++实现堆栈和队列
一.堆栈 堆栈是编程中有用的数据结构.就像一堆盘子彼此叠放. 堆栈表示类似于一堆盘子 想一想用这样一堆盘子可以做的事情 在上面放一个新盘子 卸下顶部盘子 持续来回做实验就知道了堆栈的意义 如果要 ...
- 数据结构与算法-- 广度优先打印二叉树
广度优先打印二叉树 题目:从上往下打印出二叉树的每一个节点,同一层节点按照从左到右顺序打印,例如下图中二叉树,依次打印出是8,6,10,5,7,9,11 如上题中二叉树的节点定义我们用之前文章 二叉树 ...
最新文章
- 批处理显示是上午还是下午
- Oracle修改字段类型方法
- 关于大型网站技术演进的思考(八)--存储的瓶颈终篇(8)
- windows环境的rabbitmq安装与启动
- ubuntu15.04源失效问题修复
- 制作内网yum源 同步阿里的源
- 浅谈Android项目----JSON解析(4种解析技术详解)
- weblogic时间问题
- 【JAVA】数字相加
- CS231n李飞飞计算机视觉 迁移学习之物体定位与检测上
- VMware虚拟机中Ubuntu16.04系统下通过MVS运行海康威视工业相机
- python实践项目 航空公司客户价值分析
- 必应 Bing 有点意思的搜索引擎
- 使用 React-Sketchapp
- 《Android开发偶遇有趣算法问题》---获得地图方圆r米内的一个随机坐标点(求单位圆内随机点)
- 联想笔记本一键还原出现 “系统分区结构发生改变,无法进行恢复操作”怎样处理。
- Diagnostic Viewer 显示空白
- SSO(Single Sign On)系列(一)--SSO简介
- 从零学Java(19)之 if else分支结构详解,小AD要搞对面心态!
- P1417 烹调方案