二叉树讲解升级版

目录

二叉树的存储结构

二叉树结点的查找和修改

二叉树结点的插入

二叉树的创建

二叉树的遍历

先序遍历

中序遍历

后序遍历

层序遍历

重建二叉树

二叉树的静态实现

二叉树的存储结构

一般来说,二叉树使用链表来定义。和普通链表的区别是,由于二叉树每个结点有两条出边,因此指针域变成了两个---分别指向左子树的根结点地址和右子树根节点地址。如果某个子树不存在,则指向NULL,其他地方和普通链表完全相同,因此又把这种链表叫做二叉链表,其定义如下:

struct node{typename data;node* lchild;node* rchild;
};

由于在二叉树建树前根节点不存在,因此其地址一般设为NULL。

node* root=NULL;

而如果需要新建结点(例如往二叉树中插入结点的时候),就可以使用下面的函数:

//生成一个新结点,v为结点权值
node* newNode(int v){node* Node=new node;Node->data=v;Node->lchild=Node->right=NULL;return Node;
} 

二叉树的常用操作有以下几个:二叉树的建立,二叉树结点的查找、修改、插入与删除,其中删除操作对不同性质的二叉树区别比较大,这里不做介绍。

二叉树结点的查找和修改

查找操作是指在给定数据域的条件下,在二叉树中找到所以数据域为给定数据域的结点,并将它们的数据域修改为给定的数据域。

需要使用递归来完成查找修改操作。在这里,递归式是指对当前结点的左子树和右子树分别进行递归,递归边界是当前结点为空时到达死胡同。

void search(node* root,int x,int newdata){if(root==NULL){return;}if(root->data==x){root->data=newdata;}search(root->lchild,x,newdata);search(root->rchild,x,newdata);
}

二叉树结点的插入

二叉树结点的插入位置就是数据域在二叉树中查找失败的位置。而由于这个位置是确定的,因此在递归查找的过程中一定是只根据二叉树的性质来选择左子树或右子树中的一棵子树进行递归,且最后到达空树的地方就是查找失败的地方。

void insert(node* &root,int x){if(root==NULL){root=newNode(x);return;}if(由二叉树的性质,x应该插在左子树){insert(root->lchild,x);}else{insert(root->rchild);}
}

二叉树的创建

二叉树的创建其实就是二叉树结点的插入过程,而插入所需要结点的数据域一般都会由题目给出,因此比较常用的写法是把需要插入的数据存储在数组中,然后再将它们使用insert函数一个个插入二叉树中,并最终返回根节点的指针root。

node* Create(int data[],int n){node* root=NULL;for(int i=0;i<n;i++){insert(root,data[i]);}return root;
}

二叉树的遍历

先序遍历

void preorder(node* root){if(root==NULL){return;}printf("%d\n",root->data);preorder(root->lchild);preorder(root->rchild);
}

中序遍历

void inorder(node* root){if(root==NULL){return;}inorder(root->lchild);printf("%d\n",root->data);inorder(root->rchild);
}

后序遍历

void postorder(node* root){if(root==NULL){return;}postorder(root->lchild);postorder(root->rchild);printf("%d\n",root->data);
}

层序遍历

void layerorder(node* root){queue<node*> q;//注意队列里存地址 q.push(root);while(!q.empty()){node* now=q.front;q.pop();printf("%d",now->data);if(now->lchild!=NULL){q.push(now->lchild);}if(now->rchild!=NULL){q.push(now->rchild);}}
}

在这里使用node*型变量,这样就可以通过访问地址去修改原元素,如果使用node型,队列中保存的只是元素的一个副本,因此如果队列中直接存放node型,当需要修改队首元素时,就无法修改

另外还需要指出,如果题目中要求计算出每个结点所处的层次,这时就需要在二叉树结点的定义中添加一个记录层次layer的变量:

struct node{int data;int layer;node* lchild;node* rchild;
};

需要在根节点入队前就先令根节点的layer为1来表示根节点在第一层,之后在now->lchild和now->rchild入队前,把它们的层号都记为当前结点now的层号加1

void Layerorder(node* root){queue<node*> q;root->layer=1;q.push(root);while(!q.empty()){node* now=q.front();q.pop();printf("%d ",now->data);if(now->lchild!=NULL){now->lchild->layer=now->layer+1;q.push(now->lchild);}if(now->rchild!=NULL){now->rchild->layer=now->layer+1;q.push(now->rchild);}}
}

重建二叉树

给定一棵二叉树的先序遍历序列和中序遍历序列,重建这棵二叉树。

根据二叉树遍历的性质,先序遍历的第一个结点为二叉树的根节点,中序遍历的序列可以根据二叉树的根节点将中序遍历序列划分为左子树和右子树。因此只需要在中序遍历序列中找到根节点的位置,然后划分为两个子树,然后再两个子树中分别递归执行上面的操作。

node* create(int prel,int prer,int inl,int inr){if(prel>prer){return NULL;}node* root=new node;root->data=pre[prel];int k;for(k=inl;k<=inr;k++){if(in[k]==pre[prel]){break;}}int numleft=k-inl;root->lchild=create(prel+1,prel+numleft,inl,k-1);root->rchild=create(prel+numleft+1,prer,k+1,inr);return root;
}

另外如果给定了后序序列和中序序列也可以构建一棵二叉树,做法是一样的。

例题:给出一棵树的后序遍历序列和中序遍历序列,求这棵二叉树的层次遍历序列。

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn = 50;
struct node{int data;node* lchild;node* rchild;
};
int pre[maxn],in[maxn],post[maxn];
int n;
node* create(int postl,int postr,int inl,int inr){if(postl>postr){return NULL;}node* root=new node;root->data=post[postr];int k;for(k=inl;k<=inr;k++){if(in[k]==post[postr]){break;}}int numleft=k-inl;root->lchild=create(postl,postl+numleft-1,inl,k-1);root->rchild=create(postl+numleft,postr-1,k+1,inr);return root;
}
int num=0;
void bfs(node* root){queue<node*> q;q.push(root);while(!q.empty()){node* now=q.front();q.pop();printf("%d",now->data);num++;if(num<n){printf(" ");}if(now->lchild!=NULL){q.push(now->lchild);}if(now->rchild!=NULL){q.push(now->rchild);}}
}
int main(){scanf("%d",&n);for(int i=0;i<n;i++){scanf("%d",&post[i]);}for(int i=0;i<n;i++){scanf("%d",&in[i]);}node* root=create(0,n-1,0,n-1);bfs(root);return 0;
}

二叉树的静态实现

可以使用数组来实现上面的操作,采用静态二叉链表,结点的左右指针域使用int型代替,用来表示左右子树的根节点在数组中的下标。为此需要建立一个大小为结点上限个数的node型数组,所有动态生成的结点都直接使用数组中的结点,所有对指针的操作都改为对数组下标的访问。于是,结点node的定义变为如下:

struct node{typename data;int lchild;int rchild;
}Node[maxn];

在这样的定义下,结点的动态生成就可以转变为如下的静态指定

int index=0;
int newNode(int v){Node[index].data=v;Node[index].lchild=-1;Node[index].rchild=-1;return index++;
}

下面给出二叉树的查找、插入、建立的代码。

void search(int root,int x,int newdata){if(root==-1){return;}if(Node[root].data==x){Node[root].data=newdata;}search(Node[root].lchild,x,newdata);search(Node[root].rchild,x,newdata);
}
void insert(int &root,int x){if(root==-1){root=newNode(x);return;}if(由二叉树的性质x应该插在左子树){insert(Node[root].lchild,x);}else{insert(Node[root].rchild,x);}
}
int Create(int data[],int n){int root=-1;for(int i=0;i<n;i++){insert(root,data[i]);}return root;
}

关于二叉树的先序遍历、中序遍历、后序遍历、层次遍历也做相应的转换:

void Layerorder(int root){queue<int> q;q.push(root);while(!q.empty()){int now=q.front();q.pop();printf("%d ",Node[now].data);if(Node[now].lchild!=-1){q.push(Node[now].lchild);}if(Node[now].rchild!=-1){q.push(Node[now].rchild);}}
}

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

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

相关文章

【Java】解决Java报错:StackOverflowError

文章目录 引言1. 错误详解2. 常见的出错场景2.1 无限递归2.2 递归深度过大2.3 方法调用层次过深 3. 解决方案3.1 优化递归算法3.2 尾递归优化3.3 增加调用栈大小3.4 检查递归终止条件 4. 预防措施4.1 使用迭代替代递归4.2 尾递归优化4.3 合理设计递归算法4.4 调整JVM参数4.5 定…

b端系统类管理平台设计前端开发案例

b端系统类管理平台设计前端开发案例

二叉树-堆的详解

一&#xff0c;树的概念 1&#xff0c;树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。 把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 有…

vue3 + echarts 二次开发百分比饼图

效果图&#xff1a; 安装 pnpm i echarts 公共模块组件 <divclass"pie"ref"percent"style"width: 100%; height: calc(100% - 48px)"></div> import { ref, onMounted } from vue import * as echarts from echarts const prop…

【JavaScript脚本宇宙】解密前端工具:选择最佳JavaScript模块管理工具

精选前端工具汇总&#xff1a;打包器和捆绑器的完整指南 前言 在现代Web开发中&#xff0c;使用适当的工具和库可以极大地提高开发效率和项目质量。本文将介绍一些常用的Web应用程序捆绑器&#xff0c;这些工具能够帮助开发人员有效地管理JavaScript模块和资源。 欢迎订阅专栏…

SpringBoot项目启动提示端口号占用

Windows环境下&#xff0c;SpringBoot项目启动时报端口号占用&#xff1a; *************************** APPLICATION FAILED TO START ***************************Description:Web server failed to start. Port 8080 was already in use.Action:Identify and stop the proc…

【乐吾乐3D可视化组态编辑器】状态告警示例

状态告警的设置方法为两种&#xff1a; 1.通过数据点号设置&#xff08;推荐&#xff09;&#xff1a; 适用于绑定单一数据点号&#xff0c;设置逻辑简洁&#xff0c;实现简单逻辑交互 2.通过交互事件监听数据点号设置&#xff1a; 适用于绑定多个数据点号&#xff0c;实现复…

LLM大模型AI应用的三阶技术

第一阶 指令工程&#xff08;Prompt Enginner&#xff09; 设计提示&#xff08;Prompt Design&#xff09; 结果优化&#xff08;Response Optimization&#xff09; 交互设计&#xff08;Interaction Design&#xff09; 模型理解&#xff08;Model Understanding&#…

哈希经典题目(C++)

文章目录 前言一、两数之和1.题目解析2.算法原理3.代码编写 二、判定是否互为字符重排1.题目解析2.算法原理3.代码编写 三、 字⺟异位词分组1.题目解析2.算法原理3.代码编写 总结 前言 哈希表是一个存储数据的容器&#xff0c;我们如果想要快速查找某个元素&#xff0c;就可以…

Python驱动下的AI革命:技术赋能与案例解析

在当今这个信息化、数据化的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经成为推动社会发展的重要力量。而Python&#xff0c;作为一种简单易学、功能强大的编程语言&#xff0c;在AI领域的应用中发挥着至关重要的作用。本文将探讨Python在AI领域的应用、其背后的技…

MMUNet:形态学特征增强网络在结肠癌病理图像分割中的应用

MMUNet: Morphological feature enhancement network for colon cancer segmentation in pathological images. 发表在&#xff1a;Biomedical Signal Processing and Control2024--影响因子&#xff1a;3.137 南华大学的论文 论文地址&#xff1a;main.pdf (sciencedirecta…

Wakeup Source框架设计与实现

Wakeup Source 为系统组件提供了投票机制&#xff0c;以便低功耗子系统判断当前是否可以进入休眠。 Wakeup Source(后简称&#xff1a;WS) 模块可与内核中的其他模块或者上层服务交互&#xff0c;并最终体现在对睡眠锁的控制上。 1. 模块功能说明 WS的处理逻辑基本上是围绕 com…

后端进阶-分库分表

文章目录 为什么需要分库为什么需要分表 什么时候需要分库分表只需要分库只需要分表 分库分表解决方案垂直分库水平分库垂直分表水平分表 分库分表常用算法范围算法hash分片查表分片 分库分表模式客户端模式代理模式 今天跟着训练营学习了分库分表&#xff0c;整理了学习笔记。…

机器学习模型进行预测和回测

这段代码是为了并行地处理多个 CSV 文件&#xff0c;并使用机器学习模型进行预测和回测。主要涉及以下步骤&#xff1a; 初始化环境与设置&#xff1a; 引入必要的库&#xff0c;如 ray 用于并行计算&#xff0c;pandas 用于数据处理&#xff0c;tqdm 用于进度条显示等。设置一…

golang 不用sleep如何实现实现每隔指定时间执行一次for循环?

今天介绍的是在go语言里面不用time.Sleep&#xff0c; 使用for range 定时器管道 来实现按照我们指定的时间间隔来执行for循环, 即&#xff1a; for range ticker.C { } 这样就实现了for每隔指定时间执行一次&#xff0c;除非管道被关闭&#xff0c;否则for而且会一直柱塞当前线…

浅说线性DP(下)

声明 最近博主身体不适&#xff0c;更新较慢&#xff0c;请大家体谅体谅 最大上升子序列 【题目描述】 一个数的序列 你的任务&#xff0c;就是对于给定的序列&#xff0c;求出最大上升子序列和。注意&#xff0c;最长的上升子序列的和不一定是最大的&#xff0c;比如序列(1…

03-3.3.1 栈在括号匹配中的应用

&#x1f44b; Hi, I’m Beast Cheng&#x1f440; I’m interested in photography, hiking, landscape…&#x1f331; I’m currently learning python, javascript, kotlin…&#x1f4eb; How to reach me --> 458290771qq.com 喜欢《数据结构》部分笔记的小伙伴可以订…

echarts的使用

一 echarts的使用 引入 echarts.js 文件 <script src"https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script> 准备一个呈现图表的盒子 <div class"container"><div class"t_header"><span>端午…

东方博宜1760 - 整理抽屉

题目描述 期末考试即将来临&#xff0c;小T由于同时肩负了学习、竞赛、班团活动等多方面的任务&#xff0c;一直没有时间好好整理他的课桌抽屉&#xff0c;为了更好地复习&#xff0c;小T首先要把课桌抽屉里的书分类整理好。 小T的抽屉里堆着 N 本书&#xff0c;每本书的封面上…

智能视频监控平台LntonCVS视频融合共享平台保障露营安全解决方案

在当今社会&#xff0c;都市生活的快节奏和压力使得越来越多的人渴望逃离城市的喧嚣&#xff0c;寻求一种短暂的慢生活体验。他们向往在壮丽的山河之间或宁静的乡村中露营&#xff0c;享受大自然的宁静与美好。随着露营活动的普及&#xff0c;露营地的场景也变得更加丰富多样&a…