数据结构初阶 —— 树(二叉树)

news2024/11/23 20:11:49

目录

一,二叉树

特殊二叉树

二叉树的性质

二叉树的存储结构 

 二,二叉树链式结构

二叉树的遍历(四种)

二叉树接口

试题


一,二叉树

  • 由一个根节点,加上两颗左二叉树和右二叉树组成,可以为空;
  • 二叉树度不大于2;
  • 二叉树子树有左右之分,次序不可颠倒,因此二叉树是有序树;

特殊二叉树

  • 满二叉树,若每层节点数都达到最大值,如有k层,则节点总数为2^{k}-1
  • 完全二叉树,如有k层,k-1层为满,最后一层从左到右为连续的二叉树;完全二叉树是效率很高的数据结构,是由满二叉树引出来的,满二叉树是特殊的完全二叉树;

二叉树的性质

  • 若根节点为1层,则一颗非空二叉树第k层上最多2^{k-1}个节点;
  • 若根节点为1层,则深度为h的二叉树最多2^{h} - 1个节点;
  • 若根节点为1层,则具有n个节点的满二叉树,其深度为
  • 任何一个二叉树,叶节点个数比度为2的节点数多1个;
  • 对于具有n个节点的二叉树,如按从上到下、从左到右的数组顺序对所有节点从0编号,则对序号为i的节点,如下:

二叉树的存储结构 

  •  一般可使用两种结构存储,顺序结构或链式结构;

顺序存储

  • 顺序存储即为数组存储,一般只适用表示完全二叉树,不完全二叉树会有空间浪费;
  • 实际使用中,只有(一种二叉树)才会使用数组来存储;
  • 物理上为数组,逻辑上是一颗二叉树;

注:这里的堆和虚拟进制地址空间中的堆是两回事,一个是数据结构,一个是管理内存的一块区域分段;

 链式存储

  • 用链表来表示元素的逻辑关系,通常链表中每个节点有三个域组成(数据域、左右指针域);
  • 链式结构可分为二叉链和三叉链;

//二叉链
typedef int BTDataType;
struct BinaryTreeNode
{
	struct BinaryTreeNode* _pLeft; //指向左孩子节点
	struct BinaryTreeNode* _pReft; //指向右孩子节点
	BTDataType _data; //值域
}BinaryTreeNode;
//三叉链
typedef int BTDataType;
struct BinaryTreeNode
{
    struct BinaryTreeNode* _pParent; //指向父节点
	struct BinaryTreeNode* _pLeft; //指向左孩子节点
	struct BinaryTreeNode* _pReft; //指向右孩子节点
	BTDataType _data; //值域
}BinaryTreeNode;

 二,二叉树链式结构

  • 普通二叉树的增删查改,意义不大;
  • 普通二叉树+搜索树规则,增删查改才有价值;
//二叉树链式结构
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType _data;
	struct BinaryTreeNode* _left;
	struct BinaryTreeNode* _right;
}BTNode;

//创建节点
BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("BuyNode");
		exit(-1);
	}
	node->_data = x;
	node->_left = node->_right = NULL;
	return node;
}

//自定义二叉树
BTNode* CreatBinaryTree()
{
	BTNode* nodeA = BuyNode('A');
	BTNode* nodeB = BuyNode('B');
	BTNode* nodeC = BuyNode('C');
	BTNode* nodeD = BuyNode('D');
	BTNode* nodeE = BuyNode('E');
	BTNode* nodeF = BuyNode('F');
	nodeA->_left = nodeB;
	nodeA->_right = nodeC;
	nodeB->_left = nodeD;
	nodeC->_left = nodeE;
	nodeC->_right = nodeF;
	return nodeA;
}

二叉树的遍历(四种)

  • 前序遍历,根 \rightarrow 左子树 \rightarrow 右子树;
  • 中序遍历,左子树 \rightarrow 根 \rightarrow 右子树;
  • 后序遍历,左子树 \rightarrow 右子树 \rightarrow 根;
  • 层序遍历,一层一层遍历;

注:深度优先遍历(前序、中序、后序),广度优先遍历(层序);

1,前序遍历

//前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)
		return;
	printf("%c ", root->_data);
	PreOrder(root->_left);
	PreOrder(root->_right);
}

2,中序遍历

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
		return;
	InOrder(root->_left);
	printf("%c ", root->_data);
	InOrder(root->_right);
}

3,后序遍历

//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
		return;
	PostOrder(root->_left);
	PostOrder(root->_right);
	printf("%c ", root->_data);
}

4,层序遍历

//层序遍历-利用队列
//一个节点出列,入列其子节点
typedef struct BinaryTreeNode* QDataType;
typedef struct QueueNode
{
    QDataType data;
    struct QueueNode* next;
}QueueNode;
typedef struct Queue
{
    QueueNode* phead;
    QueueNode* ptail;   
}

void LevelOrder(BTNode* root)
{
    Queue q;
    QueueInit(&q);
    if(root)
        QueuePush(&q, root);
    while(!QueueEmpty(&q))
    {
        BTNode* front = QueueFront(&q);
        QueuePop(&q);
        printf("%c ", front->val);
        if(front->left)
            QueuePush(&q, front->left);
        if(front->right)
            QueuePush(&q, front->right);
    }
    printf("\n");
    QueueDestroy(&q);
}

二叉树接口

//求二叉树节点个数-递归
//方法一,全局变量或static
int size = 0; 
void BinaryTreeSize(BTNode* root)
{
	if (root)
		size++;
	else
		return;
	BinaryTreeSize(root->_left);
	BinaryTreeSize(root->_right);
}

//方法二,局部变量-传址
void BinaryTreeSize(BTNode* root, int* psize)
{
	if (root)
		(*psize)++;
	else
		return;
	BinaryTreeSize(root->_left, psize);
	BinaryTreeSize(root->_right, psize);
}

//方法三,返回值
int BinaryTreeSize(BTNode* root)
{
	if (root)
		return 1 + BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right);
	else
		return 0;
}
//求二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	if (root->_left == NULL && root->_right == NULL)
		return 1;
	return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}
//求二叉树第k层节点个数
//当前树第K层节点个数 = 其左子树的第K-1层节点个数 + 其右子树的第K-1层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}
//求二叉树深度
//当前树深度 = max(左子树深度, 右子树深度) + 1;
int BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)
		return 0;
	int leftDepth = BinaryTreeDepth(root->_left);
	int rightDepth = BinaryTreeDepth(root->_right);
	return leftDepth > rightDepth ? (1 + leftDepth) : (1 + rightDepth);
}
//二叉树查找值为x的节点
//先当前节点查找,没有,在去左子树查找,没有,在取右子树查找
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->_data == x)
		return root;

	BTNode* retLeft = BinaryTreeFind(root->_left, x);
	if (retLeft)
		return retLeft;

	BTNode* retRight = BinaryTreeFind(root->_right, x);
	if (retRight)
		return retRight;

	return NULL;
}
//二叉树的销毁
void BinaryTreeDestory(BTNode* root)
{
    if(root==NULL)
        return;
    BinaryTreeDestory(root->left);
    BinaryTreeDestory(root->right);
    free(root);
}
//判断二叉树是否是完全二叉树
//利用层序,空也入列,完全二叉树非空是连续的
bool BinaryTreeComplete(BTNode* root)
{
    Queue q;
    QueueInit(&q);
    if(root)
        QueuePush(&q, root);
    while(!QueueEmpty(&q))
    {
        BTNode* front = QueueFront(&q);
        QueuePop(&q);
        if(front == NULL)
            break;
        QueuePush(&q, front->left);
        QueuePush(&q, front->right);
    }
    while(!QueueEmpty(&q))
    {
        BTNode* front = QueueFront(&q);
        QueuePop(&q);
        if(front)
        {
            QueueDestroy(&q);
            return false; 
        }
    }
    QueueDestroy(&q);
    return true;
}

试题

  • 二叉树的前序遍历(设置子程序);
  • 二叉树的中序变量(设置子程序);
  • 二叉树的后序遍历(设置子程序);
  • 单值二叉树;
  • 两颗树是否相等(时间复杂度O(N)、空间复杂度即高度O(N));
  • 另一颗树的子树;
  • 对称二叉树;
  • 根据指定前序遍历的字符串,重构此二叉树;

注:完全二叉树O(log(N));

(前序/后序:可得到根,中序:可得到左右树的区间)

  • 前序+中序,可重建树;
  • 后序+中序,可重建树;
  • 前序+后序,不可重建树;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/472790.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python cms建站教程:Wagtail建站(一、安装与基本使用)

最近有个建站的项目,因为python比较熟,为了快速建站想着用cms,但发现网上关于python cms的教程很少,于是自己试着写一个。建站工具采用Wagtail,是一款基于Django框架的cms,自己照着文档摸索了一番&#xff…

线性调频Z变换 CZT

文章目录 【1. 原理】【2. z k z_k zk​ 所在的路径】【3. CZT的实现步骤】【4. CZT的特点 】【5. CZT的应用】5.1 通过 CZT 变换求 DFT5.2 对信号的频谱进行细化分析5.3 求解Z变换X(z)的零、极点5.4 使用CZT进行Keystone变换 【6.相关文献】 线性调频Z变换(chirp …

FL Studio中文版V21的主要功能与下载教程

FL Studio21最新版是流行的数字音频工作站(DAW)其最新版本FL Studio 21,主要功能和下载教程如下: FL Studio21中文版功能介绍: 1. 全新界面:采用简洁现代的设计风格,工具栏和菜单进行重组,更加直观。提供智能提示与工作流指导,易于学习和操作。 2. 多显示器支持:可以在不同屏…

野火STM32电机系列(六)Cubemx配置ADC规则和注入通道

前文已经配置了GPIO、编码器 本节讲解CubeMXADC规则和注入通道 本文adc注入通道采用定时器触发,因此在上文定时器配置的基础上进行 常规信号(温度等)使用带DMA的常规通道连续采样 注入采样由定时器触发,采集电机三相电流&…

科大版中国版ChatGPT来啦!抢先体验

随着文心一言、通义千问等国内顶尖级ChatGPT大模型相继问世,具有语言理解和生成能力的人工智能正在引领行业创新发展。作为人工智能公司中的佼佼者,科大讯飞也开始加入到这场竞争中来。 4月20日,科大讯飞宣布即将于5月6日正式发布其最新的“…

【QT5:CAN卡通信的上位机-代码练习-收发数据+布局+引用外部库+基础样例(1)】

【QT5:CAN卡通信的上位机-代码练习-收发数据布局引用外部库基础样例1】 1、概述2、实验环境3、自我总结和提升4、事先声明5、效果展示6、代码编写过程(1)操作步骤部分1、新建工程2、加入外部库,并且加入qt工程中3、ui页面布局4、代码练习5、运…

荔枝派Zero(全志V3S)基于QT实现在LCD显示图片

文章目录 前言一、配置 buildroot 及编译二、写 QT 代码三、编译可执行文件四、拷贝到 SD 卡五、上板子测试六、资源自取 前言 有这样一个需求,通过配置 QT,在 linux 下实现显示我所想要显示的图片,实现的方式是我可以在命令行将图片的路径作…

人工智能之配置环境教程二:在Anaconda中创建虚拟环境安装GPU版本的Pytorch及torchvision并在VsCode中使用虚拟环境

人工智能之配置环境教程二:在Anaconda中创建虚拟环境安装GPU版本的Pytorch及torchvision并在VsCode中使用虚拟环境 作者介绍一. 查看自己电脑的CUDA版本1.1 方式一1.2 方式二 二. 下载安装CUDA三. 查看环境变量四. 创建虚拟环境4.1 使用指令创建虚拟环境4.2 查看Anc…

【刷题之路Ⅱ】LeetCode 24. 两两交换链表中的节点

【刷题之路Ⅱ】LeetCode 24. 两两交换链表中的节点 一、题目描述解题1、方法1——递归1.1、思路分析1.2、代码实现 2、方法2——迭代2.1、思路分析2.2、代码实现 3、方法3——交替的尾插3.1、思路分析3.2、代码实现 一、题目描述 原题连接: 24. 两两交换链表中的节…

人群计数经典方法Density Map Estimation,密度图估计

(3)Density Map Estimation(主流) 这是crowd counting的主流方法 传统方法不好在哪里?object detection-based method和regression-based method无法从图像中提取更抽象的有助于完成人群计数任务的语义特征 概况&…

asp.net个人信息管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net个人信息管理系统 是一套完善的web设计管理系统,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为vs2010,数据库为sqlserver2008,使用c#语言 开发 asp.net个人信息管理系统VS开发s…

第七章 单行函数

MySQL系列文章目录 http://t.csdn.cn/YTPe9 文章目录 MySQL系列文章目录前言一、函数的理解1、 什么是函数2 、不同DBMS函数的差异3 、MySQL的内置函数及分类 二、 数值函数1 、基本函数2、 角度与弧度互换函数3、 三角函数4、 指数与对数5、进制间的转换 三、字符串函数四、日…

音视频八股文(8)-- h264 AnnexB

NALU(Network Abstract Layer Unit) ⾳视频编码在流媒体和⽹络领域占有重要地位;流媒体编解码流程⼤致如下图所示: H264简介 H.264从1999年开始,到2003年形成草案,最后在2007年定稿有待核实。在ITU的标准⾥称为H.264&#xff0c…

Docker快速创建一个单机版的Jenkins实例

目录 一、创建 Jenkins 实例 二、Pipeline 项目 三、总结 谈到 CI/CD,那便少不了这里面的佼佼者 Jenkins,正如 Jenkins 官网说的一样:“Build great things at any scale”,构建伟大,无所不能! 话不多说…

常用传感器-温湿度传感器SHT30

SHT30温湿度传感器 基本信息 工作电压:2.4V-5.5V 通讯协议:IIC 湿度测量范围:0-100 温度测量范围:-40-125 引脚 VIN:电源正极GND:电源负极SCL:数据线SDA:数据线 操作 1.加载库…

digiKam 8.0发布,带来了许多新的功能和改进

导读digiKam 8.0开源、跨平台、免费的专业照片管理软件现在可以下载,这是一个重大更新,带来了许多新的功能和改进。 digiKam 8.0最大的变化是,该软件被移植到最新的Qt 6开源应用框架,以获得更现代和面向未来的体验。然而&#xff…

MATLAB矩阵运算

文章目录 前言一、矩阵的输入与结构操作1-1 矩阵输入1-2 矩阵扩充(1)扩充行(2)扩充列(3)其他命令(4)矩阵的修改和元素的删除 1-3 特殊矩阵生成 二、矩阵的计算2-1 、矩阵的基本运算2…

详解时间复杂度和空间复杂度问题

前言:本来我并不认为时间复杂度和空间复杂的有多重要,只要日常会判断和分析算法的复杂度即可,但是,不论是在考研的数据结构与算法中,还是在日常的刷题中,我们都会见到,限制我们时间和空间复杂度…

RK3588平台开发系列讲解(进程篇)Linux中进程的一生

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、Linux 系统中进程的一生二、Linux 系统中的进程树三、Linux 进程的分类四、进程优先级五、进程系统调用沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍 Linux 进程的相关知识。 一、Linux 系统…

vscode开发wdk

findwdk https://github.com/SergiusTheBest/FindWDK.git 拷贝FindWdk.cmake模块到下面路径中 CMake\share\cmake-3.22\Modules 注意定义系统变量或者修改下面路径,确保能找到wdk工具路径 构建项目 cmake中构建项目通过wdk_add_driver实现 wdk_add_driver(simp…