【图解数据结构】树和二叉树全面总结

简介: 阅读本次文章你将会掌握树和二叉树的基本概念、五大性质、判断完全二叉树、满二叉树,二叉树的四大遍历、要求写出遍历顺序和相应的递归代码。搞懂二叉树和树的相互转换流程。

一、前言


  • 学习目标1:掌握树和二叉树的基本概念、五大性质、判断完全二叉树、满二叉树。
  • 学习目标2:记住二叉树的四大遍历、要求写出遍历顺序和相应的递归代码。搞懂二叉树和树的相互转换流程
  • 重点:二叉树的遍历性质、二叉树和树的相互转换


二、概念及定义

1.树

什么叫树?是下面这个?

image.png抽象很到位,但经历过数据结构的人,下面这张图更加到位哦:

image.png


请理清一下上面这张图的人物关系:

上面这张图只有一个根节点,祖父作为根可以叫做大根堆,而你作为根只能叫做小根堆。向下发散出不同的结点,一个结点下面连着几个线叫做,而下面没有了结点就称为叶子

同一层的叫兄弟结点,下一层的叫孩子节点。有几代人就有几个层次,层次最大值叫做这个家族的高度,生的孩子数目最多的叫做这个家族的


2.二叉树


二叉树,二叉树字面意思就是一个树只能分两个叉。左面的叉叫做左孩子,右面的叉叫做右孩子。

官方术语: 满足每个结点度不大于2,孩子结点次序确定的树。


3.满二叉树




image.png


满二叉树就是满的,意思是每一层都是最大的结点,不能有空。


4.完全二叉树

  • 结点按照编号从左到右依次构建二叉树,不存在无左孩子、却有右孩子的情况(有就不是)
  • 满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树


三、二叉树的性质


俗话说:性质学得好,弯路少走一半!


1.层结点

在二叉树的第i层上最多有2^{i-1}个结点(i>=1)


2.总结点

深度为k的二叉树最多有2^{k}-1个结点(k>=1)


3.深度

image.png


向下取整,比如4.5向下取整为4


4.结点数

对于任意一棵二叉树,度为0的结点数等于度为2的结点数+1。


5.孩子结点

image.png


结点为i双亲结点为i/2向下取整,左孩子2* i,右孩子2*i+1

上面的性质我就不推导了,其实就是实在不想写了。

四、二叉树的遍历


1.先序遍历

15.png


算法讲解

  • 遍历顺序:根结点->左子树->右子树
  • 动态图解:和上面的动态图一样,先序遍历就像一个小人从根结点开始,围绕二叉树的外圈开始跑(遇到缝隙就钻进去),按照跑的顺序,依次输出序列


递归代码

voidPreOrder(BiTreeroot) 
/*先序遍历二叉树, root为指向二叉树(或某一子树)根结点的指针*/{
if (root!=NULL)
    {
Visit(root->data);  /*访问根结点*/PreOrder(root->LChild);  /*先序遍历左子树*/PreOrder(root->RChild);  /*先序遍历右子树*/    }
}

2.中序遍历

算法讲解

  • 遍历顺序:左子树->根结点->右子树
  • 动态图解:中序遍历就像投影仪一样,将二叉树从最左侧到最右侧依次投影到同一水平线上面,得到的从左到右的相关序列就是二叉树的中序遍历


递归代码

voidInOrder(BiTreeroot)  
/*中序遍历二叉树, root为指向二叉树(或某一子树)根结点的指针*/{
if (root!=NULL)
    {
InOrder(root->LChild);   /*中序遍历左子树*/Visit(root->data);        /*访问根结点*/InOrder(root->RChild);   /*中序遍历右子树*/    }
}



3.后序遍历

看后序遍历的动态图图之前,还记不记得先序遍历的动态图?不会忘了吧。

后序遍历就是在先序遍历的基础之上,进行像剪葡萄一样的操作,如下图:


image.gif


算法讲解

  • 遍历顺序:左子树->右子树->根结点
  • 动态图解: 后序遍历也是按照先序遍历的顺序输出,不过后序遍历就像剪葡萄,只能一个个剪,不能让超过1个的葡萄一起掉下来,那就错了。例如上图中的B,剪去B后面的D、E、H、I、J都会掉下来(达咩),而H剪去只会掉下H,规律就是这个规律


递归代码

voidPostOrder(BiTreeroot)  
/*后序遍历二叉树, root为指向二叉树(或某一子树)根结点的指针*/{
if (root!=NULL)
    {
PostOrder(root->LChild);   /*后序遍历左子树*/PostOrder(root->RChild);   /*后序遍历右子树*/Visit(root->data);        /*访问根结点*/     }

4.层次遍历



image.png



算法讲解 就是一层一层的从左至右输出,算法不要求掌握

拓展

  • 树的先根遍历==二叉树的先序遍历
  • 树的中根遍历==二叉树的后序遍历
  • 树的后根遍历==二叉树的中序遍历


五、遍历算法的简单应用


1.建立二叉链表存储的二叉树

voidCreateBiTree(BiTree*bt)
//按“扩展先序遍历序列”建立二叉树的二叉链表的算法{
charch;
ch=getchar();
if(ch==.) *bt=NULL;  // 输入时以点号“. ”表示空结点。else    {
*bt=(BiTree)malloc(sizeof(BiTNode)); //生成一个新结点            (*bt)->data=ch;
CreateBiTree(&((*bt)->LChild)); //生成左子树CreateBiTree(&((*bt)->RChild)); //生成右子树    }
}


2.输出叶子结点

voidPreOrder(BiTreeroot) 
/*先序遍历二叉树, root为指向二叉树根结点的指针*/{
if (root!=NULL)
    {
if (root->LChild==NULL&&root->RChild==NULL)
printf("%c  ",root->data);  /*输出叶子结点*/PreOrder(root->LChild);  /*先序遍历左子树*/PreOrder(root->RChild);  /*先序遍历右子树*/    }
}


3.统计二叉树叶子结点数目

/* LeafCount保存叶子结点的数目的全局变量,调用之前初始化值为0 */方法一:voidleaf_a(BiTreeroot)
{
if(root!=NULL)
    {
leaf_a(root->LChild);   
leaf_a(root->RChild);
if (root->LChild==NULL&&root->RChild==NULL)
LeafCount++;
    }
}


4.求二叉树高度

intPostTreeDepth(BiTreebt)   /* 后序遍历求二叉树的高度递归算法 */{
inthl,hr,max;
if(bt!=NULL)
    {
hl=PostTreeDepth(bt->LChild);  /* 求左子树的深度 */hr=PostTreeDepth(bt->RChild);  /* 求右子树的深度 */max=hl>hr?hl:hr;              /* 得到左、右子树深度较大者*/return(max+1);               /* 返回树的深度 */    }
elsereturn(0);             /* 如果是空树,则返回0 */}


5.按树状打印二叉树

voidPrintTree(BiTreebt,intnLayer)  /* 按竖向树状打印的二叉树 */{
if(bt==NULL) return;
PrintTree(bt->RChild,nLayer+1);
for(inti=0;i<nLayer;i++)
printf("  ");
printf("%c\n",bt->data);
PrintTree(bt->LChild,nLayer+1);
}


六、线索二叉树


1.基本概念

  • 前驱和后继:在二叉树先序、中序、后序、层次遍历之后得到的序列,前一个是前驱,后一个是后继。就像排队一样,排在我前面的叫做我的前驱,排在我后面的叫做后继。思考一下:什么时候是没有前驱和后继的?
  • 线索:指向前驱或后继结点的一个指针
  • 线索化:对二叉树进行某种遍历,使之变成线索二叉树的一个过程
  • 线索二叉树:加上线索的一个二叉链表


2.基本结构

19.png


  • 孩子指针域:LChild指向左孩子,RChild指向右孩子
  • 标志域Ltag:Ltag==1,表示LChild指向左孩子,Ltag==0则表示LChild指向前驱
  • 标志域Rtag:Rtag==1,表示RChild指向左孩子,Rtag==0则表示RChild指向前驱
  • 选择题表示结点p为叶子结点的是:p->Ltag==1&&p->Rtag==1


3.结构体

typedefstructnode{    intdata;
intltag, rtag;
structnode*lchild, *rchild;
}JD;


4.建立中序线索化二叉树

中序线索化.gif


算法讲解:

  • LTag=0, LChild指向根结点
  • RTag=1, RChild指向遍历序列中最后一个结点(这个性质常常用来考填空题,要牢记啊!)
  • 遍历序列中第一个结点的LChild域和最后一个结点的RChild域都指向根结点


代码:

voidInthread(BiTreeroot)
/* 对root所指的二叉树进行中序线索化,其中pre始终指向刚访问过的结点,其初值为NULL*/{
if (root!=NULL)
    { 
Inthread(root->LChild);  /* 线索化左子树 */if (root->LChild==NULL)
        {
root->Ltag=1; 
root->LChild=pre;  /*置前驱线索 */        }
if (pre!=NULL&&pre->RChild==NULL)  /* 置后继线索 */        {
pre->RChild=root;
pre->Rtag=1;
        }
pre=root;
Inthread(root->RChild);  /*线索化右子树*/    }
}


七、树和二叉树转换


1.树 -> 二叉树

树到二叉树.gif

讲解

  • 连接所有兄弟结点
  • 对树中的每一个结点,只保留与第一个结点的连线,其它删除
  • 整棵树顺时针旋转90度


2.二叉树 -> 树

二叉树到树.gif

讲解

  • 将左孩子的右孩子、右孩子的右孩子......全部连接起来
  • 所有双亲结点删除与右孩子的连线
  • 调整一定的角度


3.森林 -> 二叉树

森林到二叉树.gif

讲解

  • 先将森林中每棵树转换成二叉树
  • 将二叉树根节点视为兄弟连接起来
  • 调整一定的角度


读到这里就全部结束了,其实说是全面总结,但一点都不全面(后面有时间再补充吧)。

相关文章
|
存储 算法 Java
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
464 10
 算法系列之数据结构-二叉树
|
算法 Java
算法系列之数据结构-Huffman树
Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。
438 3
 算法系列之数据结构-Huffman树
|
存储 自然语言处理 数据库
【数据结构进阶】AVL树深度剖析 + 实现(附源码)
在深入探讨了AVL树的原理和实现后,我们不难发现,这种数据结构不仅优雅地解决了传统二叉搜索树可能面临的性能退化问题,还通过其独特的平衡机制,确保了在任何情况下都能提供稳定且高效的查找、插入和删除操作。
929 19
|
存储 C++
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
【数据结构——树】哈夫曼树(头歌实践教学平台习题)【合集】目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:任务描述 本关任务:编写一个程序构建哈夫曼树和生成哈夫曼编码。 相关知识 为了完成本关任务,你需要掌握: 1.如何构建哈夫曼树, 2.如何生成哈夫曼编码。 测试说明 平台会对你编写的代码进行测试: 测试输入: 1192677541518462450242195190181174157138124123 (用户分别输入所列单词的频度) 预
593 14
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
536 12
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
289 10
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
695 3
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
544 5
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
462 4

热门文章

最新文章