文章目录

  • 引言
  • 一、树基础
    • 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. 没有父节点的节点称为根节点;
  3. 每一个非根节点有且只有一个父节点;
  4. 除了根节点外,每个子节点可以分为多个不相交的子树

    如果一个子节点有多个父节点,那么子树相交

下面展示一些树的术语:

  1. 节点的度:一个节点含有的子树的个数称为该节点的度;
  2. 树的度:一棵树中,最大的节点的度称为树的度;
  3. 叶子节点或终端节点:度为零的节点;
  4. 父亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
  5. 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
  6. 兄弟节点:具有相同父节点的节点互称为兄弟节点;
  7. 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  8. 树的高度或深度:树中节点的最大层次;
  9. 堂兄弟节点:父节点在同一层的节点互为堂兄弟;
  10. 节点的祖先:从根到该节点所经分支上的所有节点;
  11. 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
  12. 森林:由m(m>=0)棵互不相交的树的集合称为森林;

总结:根节点没有父亲,叶结点没有孩子,中间节点可以有一个父亲多个孩子

1.树的种类

  1. 有序树与无序树:如果将树中结点的各子树看成从左至右是有次序的(即不能互换),则称该树为有序树,否则称为无序树。
  2. 完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树,其中满二叉树的定义是所有叶节点都在最底层的完全二叉树
  3. 平衡二叉树(AVL树):当且仅当任何节点的两棵子树的高度差不大于1的二叉树;
  4. 排序二叉树(二叉查找树(英语:Binary Search Tree),也称二叉搜索树、有序二叉树);
  5. 霍夫曼树(用于信息编码):带权路径最短的二叉树称为哈夫曼树或最优二叉树;
  6. B树:一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多余两个子树。

2.树的存储与表示

  1. 顺序存储:将数据结构存储在固定的数组中,在遍历速度上有一定的优势,但因所占空间比较大,是非主流二叉树。二叉树通常以链式存储。
  2. 链式存储:可以存储,缺点是指针域指针个数不定

二、二叉树

1.二叉树的概念

  二叉树是n个结点的有限集合,该集合或者为空集,或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。二叉树天然具有递归结构,二叉树中包含二叉树。

二叉树(Binary Tree)是一种特殊的树型结构,它的特点是每个结点至多有两棵子树,且二叉树的子树有左右之分,其次序不能任意颠倒。即使树中只有一颗子树,也要区分左子树与右子树。

二叉树有五种基本形态:

  1. 空二叉树
  2. 只有一个根节点
  3. 根结点只有左子树
  4. 根结点只有右子树
  5. 根结点既有左子树又有右子树

只有3个节点时,树有如下形态

2.完全二叉树与满二叉树

  完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树,其中满二叉树的定义是所有叶节点都在最底层的完全二叉树
满二叉树举例:

完全二叉树举例:

满二叉树一定是完全二叉树,但是完全二叉树并不一定是满二叉树。两者的区别在于树的最后一层满没满。并且还要关注第d层所有节点是否从左向右连续地紧密排列,如果没有连完全二叉树都不是。

对一棵具有n个结点的二叉树按层序编号,如果编号为i的结点与同样深度的满二叉树中编号结点为i的结点在二叉树中位置完全相同,则这棵二叉树成为完全二叉树

下面总结完全二叉树的特点:

  1. 叶子结点只能出现在最下两层
  2. 最下层的叶子一定集中在左部连续位置
  3. 倒数二层,若有叶子结点,一定都在右部连续位置
  4. 如果结点度为1,则该结点只有左孩子,即不存在只有右子树的情况
  5. 同样结点数的二叉树,完全二叉树的深度最小

3.二叉树的性质

  1. 在二叉树的第iii层上至多有2i−12^{i-1}2i−1个结点(i>0)(i>0)(i>0)
  2. 深度为kkk的二叉树至多有2k−12^k - 12k−1个结点(k>0)(k>0)(k>0)
  3. 对于任意一棵二叉树TTT,如果其叶节点数为N0N_0N0​,而度数为2的结点总数为N2N_2N2​,则N0=N2+1N_0=N_2+1N0​=N2​+1;
  4. 具有n个结点的完全二叉树的深度必为 [log2n]+1[log_2n]+1[log2​n]+1

    [x]表示不大于x的最大整数

  5. 对完全二叉树,若从上至下、从左至右编号,则编号为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.二叉树的遍历

  二叉树的遍历,指的是如何按某种搜索路径遍历树中的每个结点,使得每个结点均被访问一次,而且仅被访问一次。
二叉树主要有两种遍历方式:

  1. 深度优先遍历:先往深走,遇到叶子节点再往回走。
  2. 广度优先遍历:一层一层的去遍历。

2.1 深度优先遍历

2.1.1 前序遍历

  前序遍历操作定义为:若二叉树为空,为空操作;否则根 -> 左 -> 右

  1. 访问根节点;
  2. 先序遍历左子树;
  3. 先序遍历右子树。
# 前序遍历
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 中序遍历

  中序遍历操作定义为:若二叉树为空,为空操作;否则左 -> 根 -> 右

  1. 中序遍历左子树;
  2. 访问根结点;
  3. 中序遍历右子树。
# 中序遍历
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 后序遍历

  后序遍历操作定义为:若二叉树为空,为空操作;否则左 -> 右 -> 根

  1. 后序遍历左子树;
  2. 后序遍历右子树;
  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.推导遍历结果

  1. 已知前序遍历和中序遍历序列,可以唯一确定一棵二叉树
  2. 已知后序遍历和中序遍历序列,可以唯一确定一棵二叉树

已知一棵二叉树的前序遍历序列为ABCDEF,中序遍历序列为CBAEDF,请问这颗二叉树的后序遍历结果是多少?
C -> B -> E -> F -> D -> A

二叉树的中序序列是ABCDEFG,后序序列是BDCAFGE,求前序序列
E -> A -> C -> B -> D -> G -> F


先序+中序,后序+中序都可以确定所有父节点


如果对您有帮助,麻烦点赞关注,这真的对我很重要!!!如果需要互关,请评论或者私信!


数据结构与算法python—9.二叉树及python实现相关推荐

  1. 视频教程-Python数据结构与算法面试(上)-Python

    Python数据结构与算法面试(上) 东北大学计算机专业硕士研究生,欧瑞科技创始人&CEO,曾任国内著名软件公司项目经理,畅销书作者,企业IT内训讲师,CSDN学院专家讲师,制作视频课程超过1 ...

  2. Python数据结构与算法(1.5)——Python基础之函数与异常

    Python数据结构与算法(1.5)--Python基础之函数与异常 0. 学习目标 1. 函数 1.1 自定义函数 1.2 函数与参数 1.3 函数与返回值 2. 异常处理 2.1 raise 语句 ...

  3. Python数据结构与算法(1.4)——Python基础之控制结构

    Python数据结构与算法(1.4)--Python基础之控制结构 0. 学习目标 1. 代码块与缩进 2. 条件语句 2.1 if 语句 2.2 if 语句的嵌套 2.3 断言 3. 循环 3.1 ...

  4. Python数据结构与算法(1.3)——Python基础之输入、输出与高阶赋值

    Python数据结构与算法(1.3)--Python基础之输入.输出与高阶赋值 0. 学习目标 1. 输入.输出与注释 1.1 获取用户输入 1.2 格式化输出 1.2.1 基本方法 1.2.2 fo ...

  5. Python数据结构与算法(1.2)——Python基础之变量与内置数据类型

    Python数据结构与算法(1.2)--Python基础之变量与内置数据类型 0. 学习目标 1. Python 程序的运行 1.1 Python 交互式解释器 1.2 Python 程序脚本 2. ...

  6. Python数据结构与算法(1.6)——Python基础之类与模块化

    Python数据结构与算法(1.6)--Python基础之类与模块化 0. 学习目标 1. 面向对象编程:类 1.1 面向对象编程的基本概念 1.1.1 多态 1.1.2 封装 1.1.3 继承 1. ...

  7. 数据结构与算法--死磕二叉树

    死磕二叉树 近一年都比较关注算法相关的知识,也刷了不少题,之前的文章中大多也是算法相关的文章,但是感觉每次遇到树相关的题型都不能应对自如,因此还是有必要在相关知识上下功夫,因此有此次总结,以下是所有树 ...

  8. apriori算法c++实现_经典数据结构与算法(四):Python/C/C ++实现队列类型双端队列数据结构...

    前期文章点击这里: 经典数据结构与算法(一):Python/C/C ++实现堆栈和队列 双端队列或双端队列是一种队列,其中可以从前面或后面执行元素的插入和删除.因此,它不遵循FIFO规则(先进先出). ...

  9. c++ 队列_经典数据结构与算法(一):Python/C/C ++实现堆栈和队列

     一.堆栈   堆栈是编程中有用的数据结构.就像一堆盘子彼此叠放. 堆栈表示类似于一堆盘子 想一想用这样一堆盘子可以做的事情 在上面放一个新盘子 卸下顶部盘子 持续来回做实验就知道了堆栈的意义 如果要 ...

  10. 数据结构与算法-- 广度优先打印二叉树

    广度优先打印二叉树 题目:从上往下打印出二叉树的每一个节点,同一层节点按照从左到右顺序打印,例如下图中二叉树,依次打印出是8,6,10,5,7,9,11 如上题中二叉树的节点定义我们用之前文章 二叉树 ...

最新文章

  1. 批处理显示是上午还是下午
  2. Oracle修改字段类型方法
  3. 关于大型网站技术演进的思考(八)--存储的瓶颈终篇(8)
  4. windows环境的rabbitmq安装与启动
  5. ubuntu15.04源失效问题修复
  6. 制作内网yum源 同步阿里的源
  7. 浅谈Android项目----JSON解析(4种解析技术详解)
  8. weblogic时间问题
  9. 【JAVA】数字相加
  10. CS231n李飞飞计算机视觉 迁移学习之物体定位与检测上
  11. VMware虚拟机中Ubuntu16.04系统下通过MVS运行海康威视工业相机
  12. python实践项目 航空公司客户价值分析
  13. 必应 Bing 有点意思的搜索引擎
  14. 使用 React-Sketchapp
  15. 《Android开发偶遇有趣算法问题》---获得地图方圆r米内的一个随机坐标点(求单位圆内随机点)
  16. 联想笔记本一键还原出现 “系统分区结构发生改变,无法进行恢复操作”怎样处理。
  17. Diagnostic Viewer 显示空白
  18. SSO(Single Sign On)系列(一)--SSO简介
  19. 从零学Java(19)之 if else分支结构详解,小AD要搞对面心态!
  20. P1417 烹调方案

热门文章

  1. [Swustoj 24] Max Area
  2. linux用命令行来执行php程序
  3. Hdu 1794 【二维成段更新】.cpp
  4. 【map知识点总结】
  5. 可以在函数中间打点了,以分析bpf_prog_load函数为例
  6. API文档自动生成,Swagger的配置
  7. codevs 1388 砍树
  8. linux下查看分区信息和剩余空间大小
  9. Hadoop原理深度剖析系列1——Hadoop的基本知识
  10. Linux上层应用--Shell scripts基础规范