文章目录

    • 树的定义
    • 基本术语
    • 二叉树
      • 二叉树的定义
      • 满二叉树和完全二叉树
      • 二叉树的性质
  • 二叉树的存储结构
    • 顺序存储表示
    • 链式存储表示
      • 二叉链表
      • 三叉链表
      • 线索链表
  • 遍历二叉树
    • 先序遍历
    • 中序遍历
    • 后序遍历
  • 创建二叉树
  • 统计二叉树中叶子结点的个数
  • 求二叉树树的深度
  • 主函数

树的定义

(Tree)是n(n>=0)个结点的有限集。当有限集T为空时称为空树
当T不为空时,它满足两个条件:

  1. 有且仅有一个特定的称为根(root)的结点;
  2. 其余的结点可分为m(m>=0)个互不相交的子集T1,T2,T3…Tm,其中每个子集又是一棵树,并称其为子树(Subtree)。

基本术语

  1. 树的结点包含一个数据元素及若干指向其子树的分支
  2. 度为0的结点称为叶子结点(leaf)或终端结点
  3. 结点所拥有的子树的个数称为该结点的度。结点度的最大值为树的度
  4. 结点的层次:根为第一层,根的孩子为第二层。树中结点的最大层次称为树的深度
  5. 树中一个结点的子树的根结点称为这个结点的孩子(child)。这个结点称为它孩子结点的双亲(Parent)。具有同一个双亲的孩子结点互称为兄弟(sibling)。
  6. 如果将树中结点的各子树看成从左至右是有次序的,则称该树为有序树,否则称为无序树。

二叉树

二叉树的定义

二叉树(Binary Tree)是一种特殊的树型结构,它的特点是每个结点至多只有两棵子树(即二叉树中不存在度数大于2的结点),并且,二叉树的子树有左右之分

二叉树就是度为2的树吗?

不是
一棵度为二的有序树与一棵二叉树的区别在于:有序树的结点次序是相对于另一结点而言的,如果有序树中的子树只有一个孩子时,这个孩子结点就无须区分其左右次序,而二叉树无论其孩子数是否为2,均需确定其左右次序,也就是说二叉树的结点次序不是相对于另一结点而言而是确定的。

满二叉树和完全二叉树

满二叉树:深度为k,且有(2^k)-1个结点的二叉树。

完全二叉树:深度为k,结点数为n的二叉树,当且仅当每个结点的编号都与相同深度的满二叉树中从1到n的结点一一对应时,称为完全二叉树。

下面两个二叉树不是完全二叉树,因为没有与相同深度的满二叉树中的结点一 一对应

二叉树的性质

  • 在二叉树的第i层上至多有 2i-1个结点。
  • 深度为k的二叉树至多有 2k-1个结点(k>1)。
  • 对任何一棵二叉树T, 如果其叶结点数为 n0, 度为2的结点数为 n2,则n0=n2+1。
  • 具有 n (n >=0) 个结点的完全二叉树的深度为|log2(n)|(取整)+1
  • 如果对一棵有n个结点的完全二叉树的结点按层次顺序编号,则对任一结点有:
    如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结点 i/2(取整)
    如果2i>n,则结点i无左孩子;否则其左孩子是结点2i(取整)
    如果2i+1>n,则结点i无右孩子;否则其右孩子是结点2i+1(取整)

给个图例,自行体会~

二叉树的存储结构

顺序存储表示

完全二叉树的结点层次编号,用一组地址连续的存储单元,依编号存放二叉树中的数据元素,结点的相对位置蕴含着结点之间的关系。

如图

它的存储序列为:

链式存储表示

可根据不同的结点结构设计不同形式的链式存储结构

对于下图的二叉树,有几种不同的链式存储结构

二叉链表



二叉树的类型描述

typedef char TElemType;
typedef struct BiNode{TElemType data;struct BiNode* lchild;          //左孩子struct BiNode* rchild;          //右孩子
}BiNode,*BiTree;

三叉链表



三叉链表的类型描述

typedef char TElemType;
typedef struct BiNode{TElemType data;struct BiNode* lchild;          //左孩子struct BiNode* rchild;          //右孩子struct BiNode* parent;          //父结点
}BiNode,*BiTree;

线索链表

二叉链表中的空链域存储其他有用信息

遍历二叉树

根据某种路径来巡防二叉树中的每一个结点,使得每一个结点均被访问,且只访问一次。二叉树的遍历是对二叉树进行多种操作的基础

本文采用的是二叉链表的存储结构

如下图所示,有三种遍历方式

  1. 先序遍历 DLR
  2. 中序遍历 LDR
  3. 后序遍历 LRD

再举一个例子

先序遍历

实际上是一种递归算法思想

  1. 访问根结点
  2. 先序遍历左子树
  3. 先序遍历右子树

若遍历二叉树,则需要对二叉树进行一定的操作,这里就对二叉树进行输出操作

status Print(TElemType e)
{   //输出数据值cout << e;return OK;
}void PreOrderTraverse(BiTree T, status (*Print)(TElemType e))
{  //先序遍历算法if (T){Print(T->data);PreOrderTraverse(T->lchild, Print);PreOrderTraverse(T->rchild, Print);}
}

中序遍历

  1. 中序遍历左子树
  2. 访问根结点
  3. 中序遍历右子树
void InOrder(BiTree T, status(*Print)(TElemType e))
{   //中序遍历算法if (T){InOrder(T->lchild, Print);Print(T->data);InOrder(T->rchild, Print);}
}

后序遍历

  1. 后序遍历左子树
  2. 后序遍历右子树
  3. 访问根结点
void PostOrder(BiTree T, status(*Print)(TElemType e))
{    //后序遍历算法if (T){PostOrder(T->lchild, Print);PostOrder(T->rchild, Print);Print(T->data);}
}

创建二叉树

这里默认用先序的方式创建二叉树。

思路如下:

  1. 首先判断二叉树是否为空(即输入的第一个数据a是否为’#’)。若第一个数据a为空格,则二叉树为NULL;若不为空,进行下列操作
  2. 为根建立结点,其数据域为a
  3. 先序创建左子树的二叉树链表
  4. 先序创建右子树的二叉树链表

(即在满二叉树的相应结点位置如果为空,就输入’#’)

void CreateBiTree(BiTree &T)
{   //先序创建二叉树TElemType a;cin >> a;if (a == '#') T = NULL;else {T = (BiNode*)malloc(sizeof(BiNode));if(!T) exit(0);T->data = a;CreateBiTree(T->lchild);CreateBiTree(T->rchild);}
}

假如要创建如下图的二叉树


则需要输入以下:
ABC##DE#G##F###

统计二叉树中叶子结点的个数

我们先来回顾一下叶子结点的定义:度为0的结点。
故当一个结点左孩子和右孩子都不存在时,这个结点就为叶子结点。
因为要返回叶子结点的个数,所以需在算法中增添一个“计数器”,并将算法中对结点的操作改为:若是叶子结点,则计数器加1。

注意:要为计数器专门定义一个全局变量,默认值为0,不要让它的赋值语句出现在函数体或主函数中!

int num=0;     //计数器num为全局变量
void countleaf(BiTree T, int& num)
{   //返回叶子结点的个数if (T){if (!T->lchild && !T->rchild) num++;countleaf(T->lchild, num);countleaf(T->rchild, num);}
}

求二叉树树的深度

当二叉树为空树时,深度为0;当二叉树只有根结点时,深度为1;
其他情况时,求左子树和右子树中深度的最大值,再加1,即为树的深度

int  Depth(BiTree T)
{int left, right,h;if (!T) h = 0;else if (!T->lchild && !T->rchild) h = 1;else {left = Depth(T->lchild);right = Depth(T->rchild);h = 1 + (left > right ? left : right);}return h;
}

主函数

void main()
{BiTree T;int i;T = (BiNode*)malloc(sizeof(BiNode));cout << "1.创建二叉树  2.输出(先序)  3.输出(中序)  4.输出(后序) 5.返回叶子结点的个数  6.返回二叉树的深度  7.退出" << endl;while (1){cout << "请输入操作:";cin >> i;switch (i){case 1: cout << "请输入树的结点:";  CreateBiTree(T); break;case 2: cout << "先序遍历输出为:"; PreOrderTraverse(T, Print); cout << endl; break;case 3: cout << "中序遍历输出为:"; InOrder(T, Print); cout << endl;  break;case 4: cout << "后序遍历输出为:"; PostOrder(T, Print);  cout << endl; break;case 5: cout << "叶子结点的个数为:"; countleaf(T,num);  cout<<num << endl; break;case 6: cout << "该二叉树的深度为:" << Depth(T); cout << endl;  break;case 7: exit(0); break;default: cout << "请重新输入!" << endl; break;}}
}

运行结果如下:

总而言之,二叉树中,最重要的思想是递归,并且所有操作的基础就是遍历

我总是喜欢把很多东西慢慢攒起来,等到时机成熟的时候一股脑开启,就像是专门为这一刻准备了这么久一样。这很好,但是这总会给我一种幻觉,仿佛我这一段时间,这些年,甚至这一辈子,就是为此而活的。如今我才明白,这是目标,但不是终极目标,更不是那种达成目标之后如释重负到脱离苦海的感觉。你要知道,人生的终极目标是死亡啊,生活并不是达到高潮就开始享受,堕入低谷就昏天黑日。
一生冗长,不失爱与自由,向死而行的生命都在热烈地生长。

二叉树的基本操作的实现相关推荐

  1. 二叉树的基本操作(c语言)

    二叉树的基本操作 创建二叉树 二叉树的叶子节点 二叉树的节点总数 二叉树的高度 二叉树的基本遍历方法 创建二叉树 代码: // 创建二叉树,使用# void createBiTree(BiTree&a ...

  2. 二叉树的基本操作之二叉排序树

    //二叉树的基本操作之-建立二叉排序树并遍历 #include<stdio.h> #include<string.h> struct Node{Node *lchild;Nod ...

  3. 二叉树的基本操作及哈夫曼编码/译码系统的实现

    二叉树的基本操作及哈夫曼编码/译码系统的实现 实验目的和要求 掌握二叉树的二叉链表存储表示及遍历操作实现方法. 实现二叉树遍历运算的应用:求二叉树中叶结点个数.结点总数.二叉树的高度,交换二叉树的左右 ...

  4. 树的基本操作代码 c语言,二叉树的基本操作(C语言、源代码)

    二叉树的基本操作(源代码) #include "stdio.h" #include "malloc.h" #define MAX 100 typedef str ...

  5. 手写二叉树的基本操作

    本题题目来源是: https://pta.patest.cn/pta/test/1342/exam/3/question/20490 老实说,写二叉树的基本操作写的我心慌意乱,主要原因是总感觉递归学的 ...

  6. 实现二叉树的基本操作(Java版)

    近期研究了一下二叉树,试着用Java语言实现了二叉树的基本操作,下面分享一下实现代码: package com.sf.test;import java.util.ArrayDeque; import ...

  7. 二叉树的基本操作——数据结构实验报告

    一.实验名称:二叉树 二.实验目的 1)熟练掌握二叉树的存储方式的具体实现过程,实现二叉树的基本操作及运算: 2)进一步巩固指针的用法,栈及队列的基本操作,进一步体会递归算法,学会综合应用. 三.实验 ...

  8. 数据结构实验报告,二叉树的基本操作(C语言)

    数据结构实验报告,二叉树的基本操作(C语言) 作者:命运之光 专栏:数据结构 目录 数据结构实验报告,二叉树的基本操作(C语言) 实验六 二叉树的基本操作 一.需求分析 二.概要设计 三.详细设计 四 ...

  9. 数据结构实验 二叉树的基本操作

    数据结构实验 二叉树的基本操作 实验环境: Visual C++ 实验目的: 1.掌握二叉树的定义: 2.掌握二叉树的基本操作,如二叉树的建立.遍历.结点个数统计.树的深 度计算等. 实验内容: 用递 ...

  10. C语言二叉树的基本操作(超全)

    二叉树作为数据结构其实是一个挺有意思的结构,可以有多种应用 我们直接来看一下二叉树的代码: #include<stdlib.h> #include<stdio.h> #incl ...

最新文章

  1. Otter-入门篇4(单向同步实践)
  2. 中国移动OnetNet云平台 使用以太网传输数据流步骤
  3. requests(一): 发送一个json格式的post请求
  4. Algorithm之OP:OP之GA遗传算法思路理解相关配图资料
  5. 抽象类可用于创建对象吗_野芝麻,见过吗?可用于肺热咳血、血淋、跌打损伤...
  6. 如何优雅的链式取值之 MayBe 函子
  7. 浏览器自带的前进后退按钮禁用
  8. 【生活科普】这7个影视剧的经典桥段,骗了我们很多年……
  9. [转载] Java泛型详解:<T>和Class<T>的使用。泛型类,泛型方法的详细使用实例
  10. JavaScript------表单约束验证DOM方法
  11. Utility Lambda
  12. zen3架构_曝Zen 3架构IPC性能将比Zen 2提升10~15%
  13. win10 更新后指纹不能使用
  14. 排名 教材 数字电子技术_数字电子技术教材改革及实践
  15. 2022年低压电工考试题模拟考试题库及模拟考试
  16. PotPlayer设置最小化的快捷键
  17. PNAS:植物香豆素塑造拟南芥合成根系微生物组的组成
  18. MySQL45讲 读书笔记 22讲MySQL有哪些“饮鸩止渴”提高性能的方法
  19. UOS安装 MySQL5.7
  20. BlinkOn9 - Layered APIs

热门文章

  1. 身份证OCR实名认证接口
  2. dos环境c语言写串口程序,DOS下串口通信程序来传送文件的源代码(转)
  3. proxmox ve 中文社区_Proxmox VE 部署维护
  4. Token是什么玩意
  5. 8*8LED点阵图原理(74HC595芯片使用方法)
  6. html链接加入qq群,获取QQ群加群链接
  7. Fastadmin和Easywechat
  8. SNAT DNAT
  9. 什么专业的人适合学嵌入式?
  10. vue编程式导航跳转到当前路由多次执行抛出NavigationDuplicated: Avoided redundant navigation to current location: