首页 > 其他分享 >数据结构:二叉树(2)

数据结构:二叉树(2)

时间:2024-09-21 11:20:38浏览次数:10  
标签:结点 遍历 return 二叉树 数据结构 root 节点

ps:爆更第二期


前言

普通的树的实用价值比较小,将树更一步特殊化成二叉树,将获得更多的特殊性质。
例如搜索二叉树,红黑树等。
这篇博文主要介绍二叉树的基础知识,进阶版高级二叉树,后续会持续更新。

二叉树的概念

一棵二叉树是结点的一个有限集合,
该集合:

  1. 或者为空
  2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成

老规矩,上图
在这里插入图片描述
在上一篇博客中,介绍了度的概念,
二叉树救赎一颗树,如果他的度不超过2,那他就是一颗二叉树。

二叉树的几种情况
在这里插入图片描述


我们怎么去认识一颗二叉树呢?

对于任意一颗二叉树,我们都要将其这么看:
根,左子树,右子树
左子树也要看成根,左子树,右子树

为什么这么说呢?读下去,你就懂啦

特殊的二叉树

(1)满二叉树
一棵二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是
说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。

也就是说,满二叉树的每一层都要是满的。

(2)完全二叉树
完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K
的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对
应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。(堆就是一颗完全二叉树)

也就是说,完全二叉树除了最后一层,其他层都必须是满的,而且,就算最后一层没满,也需要从左到右连续。

在这里插入图片描述

二叉树的性质

1.若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有 2i-1 个结点.
2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2n -1 .
3. 对任何一棵二叉树, 如果度为0其叶结点个数为n0 , 度为2的分支结点个数为 n2,则有 n0 = n2 +1
4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度h= log(N+1). (ps: 是log以2为底,n+1为对数)
5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:

  1. 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
  2. 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
  3. 若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子

这些都比较好理解,比较难理解的是第三点

对任何一棵二叉树, 如果度为0其叶结点个数为n0 , 度为2的分支结点个数为 n2,则有 n0 = n2 +1

为什么这么说呢?
最初的二叉树假设只有根节点,此时
n0 = 1, n2 = 0
在后续添加节点的过程中,只会出现两种情况

  1. n0 + 1, n2 + 1
  2. n0 不变,n2不变

读者可以尝试画图证明一下。

二叉树的存储

二叉树的存储分为两种情况

  1. 顺序存储
    适合用于完全二叉树,其中堆就是采用顺序存储实现。
    传送门:数据结构:堆

  2. 链式存储
    对于普通的二叉树,使用链式存储是最佳的方式。
    因为二叉树最多只有两个子节点,所以不用使用左孩子右兄弟表示法,直接用两个指针分别表示左孩子和右孩子即可。

struct Node
{
  Node* leftchild;
  Node* rightchild;
  DataType val;
};

二叉树的遍历

二叉树比较复杂,和之前的线性表遍历不太相同。
二叉树的遍历分为前序遍历,中序遍历,后续遍历,以及层序遍历。

前序遍历:根,左孩子,右孩子
中序遍历:左孩子,根,右孩子
后续遍历:左孩子,右孩子,根

前中后序遍历


作者感觉在这个地方没有讲清楚,十分抱歉


前中后序遍历的实现基于分治思想,使用递归实现(也可用非递归)
这里就到了前面说要把一棵树看成根,左子树,右子树的时候了。

以前序遍历为例,三种顺序遍历类似。
先分治成小问题:遍历根,左子树,右子树。
递归还需要结束条件,也就是最小子问题。
这里的子问题是遇到空树,千万不要想成遇到叶子结点

void prevOrder(TreeNode* root)
{
   if(root == nullptr)
   return;

   cout << root->val << endl;//根节点
   prevOrder(root->leftchild);//左子树
   prevOrder(root->rightchild);//右子树
}

在这里插入图片描述
利用这张图,可以帮助理解一下前序遍历的递归过程。

中序遍历与后序遍历类似,这里就不详细讲解了。


层序遍历

层序遍历的思路非常的巧妙
层序遍历利用队列实现

首先,将根节点进入队列,
每次将队首元素出队列,队首元素的左右非空子节点入队列
直到队列为空,则层序遍历完毕。

void LevelOrder(TreeNode* root)
{ 
    if(root == nullptr)
    return;

queue<TreeNode*> q;
q.push(root);

while(!q.empty())
{
    TreeNode* front = q.front();
    q.pop();
    cout << front->val;
    if(front->leftchild)
    q,push(front->childleft);
    if(front->rightchild)
    q.push(front->rightchild);
}

}

在这里插入图片描述


二叉树的节点个数及高度等

依旧是采用分治思想,分解成子问题,用递归的方式来解决问题。

  1. 二叉树节点个树
    其实就是左子树节点个数 + 右子树节点个数 + 1
  2. 二叉树的高度
    其实就是左右子树高度的最大值 + 1
  3. 二叉树叶子结点的个数
    其实就是左子树叶子结点个数 + 右子树叶子结点个数
  4. 二叉树中查找val = k的节点
    其实就是根节点查找,左子树查找,右子树查找
size_t size(Node* root)
{
	if (root == nullptr)
		return 0;

	return size(root->leftchild) + size(root->rightchild) + 1;
}


size_t height(Node* root)
{
	if (root == nullptr)
		return 0;

	return max(height(root->leftchild), height(root->rightchild)) + 1;
}

size_t size_leaf(Node* root)
{

	if (root == nullptr)
		return 0;
	if (root->leftchild == nullptr && root->rightchild == nullptr)
		return 1;

	return size_leaf(root->leftchild) + size_leaf(root->rightchild);
}

Node* find(Node* root, DataType val)
{
	if (root == nullptr)
		return nullptr;

	if (root->val == val)
		return root;


	Node* left = find(root->leftchild, val);
	if (left)
		return left;

	return find(root->rightchild, val);
}

标签:结点,遍历,return,二叉树,数据结构,root,节点
From: https://blog.csdn.net/2303_78940834/article/details/142412624

相关文章

  • 数据结构: 顺序表(Seqlist篇) 手把手带你了解数据结构~
    文章目录前言一、顺序表的概念1.顺序表是什么?2.顺序表和数组的区别二、顺序表的实现1.顺序表的结构(1).静态顺序表(2).动态顺序表2.顺序表的初始化3.顺序表的销毁4.顺序表尾插5.顺序表头插6.顺序表尾删7.顺序表头删8.顺序表获取元素下标9.顺序表任意位置插入......
  • 数据结构:顺序表
    顺序表顺序表的概念与结构静态顺序表动态顺序表动态顺序表的实现SeqList.h的创建测试文件(test.c)初始化动态顺序表(LS_Init)动态顺序表的销毁(LS_Destry)检查动态内存空间是否已满(SL_CheckCapacity)动态顺序表打印有效数据(SL_Print)在末尾存放数据(SL_PushBack)在起始位置添加有......
  • 数据结构 ——— 常见的时间复杂度计算例题(上篇)
    目录前言例题1:例题2:例题3:例题4:前言在上一章讲解了时间复杂度的概念,以及用大O的渐进表示法表示时间复杂度数据结构———算法的时间复杂度-CSDN博客接下来利用C语言代码的例题,更深一步的掌握用大O的渐进表示法表示代码的时间复杂度例题1:代码演示:voidFunc......
  • en造数据结构与算法C# 用Unity实现简单的群组行为算法 之 对齐
    en造数据结构与算法C#用Unity实现简单的群组行为算法之聚集-CSDN博客en造数据结构与算法C#用Unity实现简单的群组行为算法之聚集-CSDN博客演示思路1.检测自然是沿用前两节的检测范围2.对齐朝向对齐朝向就是邻居鸟的forward加起来再除总数得到平均数3.对齐速度......
  • en造数据结构与算法C# 群组行为优化 和 头鸟控制
    实现:1.给鸟类随机播放随机动画使得每一只鸟扇翅膀的频率都不尽相同2.可以自行添加权重,并在最后 sumForce=separationForce+cohesionForce+alignmentForce;分别乘上相应权重,这样鸟就能快速飞行和转向辣usingSystem.Collections.Generic;usingUnityEngine;usingS......
  • 【C++二叉树】105.从前序与中序遍历序列构造二叉树
    105.从前序与中序遍历序列构造二叉树-力扣(LeetCode)根据前序遍历和中序遍历构建二叉树前序遍历访问方式:根-左子树-右子树中序遍历访问方式:左子树-根-右子树思路分析:前序+中序可以构建一颗二叉树:前序遍历可以确定根,中序遍历可以确定左子树的中序区间和右子树的中序区......
  • 【学习笔记】数据结构(六 ①)
    树和二叉树(一)文章目录树和二叉树(一)6.1树(Tree)的定义和基本术语6.2二叉树6.2.1二叉树的定义1、斜树2、满二叉树3、完全二叉树4、二叉排序树5、平衡二叉树(AVL树)6、红黑树6.2.2二叉树的性质6.2.3二叉树的存储结构6.3遍历二叉树和线索二叉树6.3.1遍历二叉树......
  • Python中的树与图:构建复杂数据结构的艺术
    引言随着大数据时代的到来,我们面临的数据不再是简单的线性关系,而是错综复杂的网状结构。树和图正是用于表示这类复杂关系的最佳工具。树是一种特殊的图,它具有层次结构;而图则更加灵活,能够表达任意节点之间的连接关系。掌握树与图的实现方法,不仅有助于提高算法设计能力,还能为......
  • 代码随想录算法训练营第十五天 | Javascript | 继续二叉树的一天 | 力扣Leetcode | 补
    目录前言简介题目链接:501.二叉搜索树中的众数题目链接:236.二叉树的最近公共祖先题目链接:235.二叉搜索树的最近公共祖先前言踏平坎坷成大道,斗罢艰险又出发!自律的尽头是自控,自控的尽头是硬控。愿道友们披荆斩棘,终能得偿所愿。简介本人是小几年经验的前端开发,......
  • 代码随想录 -- 二叉树 -- 把二叉搜索树转换为累加树
    538.把二叉搜索树转换为累加树-力扣(LeetCode)思路:定义pre变量用来记录当前节点的前一个节点(右中左顺序遍历)的值。递归出口:当root为空时,return。单层递归逻辑:(右中左)右:self.tra(root.right)中:令root的值为它本身加上pre,更新pre为当前root的值;左:self.tra(root.left)class......