二叉树讲解升级版

2024-06-08 03:20
文章标签 二叉树 讲解 升级版

本文主要是介绍二叉树讲解升级版,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

二叉树的存储结构

二叉树结点的查找和修改

二叉树结点的插入

二叉树的创建

二叉树的遍历

先序遍历

中序遍历

后序遍历

层序遍历

重建二叉树

二叉树的静态实现

二叉树的存储结构

一般来说,二叉树使用链表来定义。和普通链表的区别是,由于二叉树每个结点有两条出边,因此指针域变成了两个---分别指向左子树的根结点地址和右子树根节点地址。如果某个子树不存在,则指向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.chinasem.cn/article/1041079

相关文章

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

leetcode105 从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。 注意: 你可以假设树中没有重复的元素。 例如,给出 前序遍历 preorder = [3,9,20,15,7]中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树: 3/ \9 20/ \15 7   class Solution {public TreeNode buildTree(int[] pr

PHP实现二叉树遍历(非递归方式,栈模拟实现)

二叉树定义是这样的:一棵非空的二叉树由根结点及左、右子树这三个基本部分组成,根据节点的访问位置不同有三种遍历方式: ① NLR:前序遍历(PreorderTraversal亦称(先序遍历)) ——访问结点的操作发生在遍历其左右子树之前。 ② LNR:中序遍历(InorderTraversal) ——访问结点的操作发生在遍历其左右子树之中(间)。 ③ LRN:后序遍历(PostorderT

ispunct函数讲解 <ctype.h>头文件函数

目录 1.头文件函数 2.ispunct函数使用  小心!VS2022不可直接接触,否则..!没有这个必要,方源一把抓住VS2022,顷刻 炼化! 1.头文件函数 以上函数都需要包括头文件<ctype.h> ,其中包括 ispunct 函数 #include<ctype.h> 2.ispunct函数使用 简述: ispunct函数一种判断字符是否为标点符号的函

深度学习速通系列:深度学习算法讲解

深度学习算法是一系列基于人工神经网络的算法,它们通过模拟人脑处理信息的方式来学习和解决复杂问题。这些算法在图像识别、语音识别、自然语言处理、游戏等领域取得了显著的成就。以下是一些流行的深度学习算法及其基本原理: 1. 前馈神经网络(Feedforward Neural Networks, FNN) 原理:FNN 是最基本的神经网络结构,它由输入层、隐藏层和输出层组成。信息从输入层流向隐藏层,最

C#设计模式(1)——单例模式(讲解非常清楚)

一、引言 最近在学设计模式的一些内容,主要的参考书籍是《Head First 设计模式》,同时在学习过程中也查看了很多博客园中关于设计模式的一些文章的,在这里记录下我的一些学习笔记,一是为了帮助我更深入地理解设计模式,二同时可以给一些初学设计模式的朋友一些参考。首先我介绍的是设计模式中比较简单的一个模式——单例模式(因为这里只牵涉到一个类) 二、单例模式的介绍 说到单例模式,大家第一

[项目][CMP][直接向堆申请页为单位的大块内存]详细讲解

目录 1.系统调用 1.系统调用 Windows和Linux下如何直接向堆申请页为单位的大块内存: VirtualAllocbrk和mmap // 直接去堆上按页申请空间static inline void *SystemAlloc(size_t kpage){#ifdef _WIN32void *ptr = VirtualAlloc(0, kpage << 13,

高斯平面直角坐标讲解,以及地理坐标转换高斯平面直角坐标

高斯平面直角坐标系(Gauss-Krüger 坐标系)是基于 高斯-克吕格投影 的一种常见的平面坐标系统,主要用于地理信息系统 (GIS)、测绘和工程等领域。该坐标系将地球表面的经纬度(地理坐标)通过一种投影方式转换为平面直角坐标,以便在二维平面中进行距离、面积和角度的计算。 一 投影原理 高斯平面直角坐标系使用的是 高斯-克吕格投影(Gauss-Krüger Projection),这是 横

在二叉树中找到两个节点的最近公共祖先(基于Java)

如题  题解 public int lowestCommonAncestor(TreeNode root, int o1, int o2) {//记录遍历到的每个节点的父节点。Map<Integer, Integer> parent = new HashMap<>();Queue<TreeNode> queue = new LinkedList<>();parent.put(roo

车险该怎么买?行业人讲解车险

很多车主对汽车保险知识不了解,稀里糊涂的买了车辆保险,但是出险时发现很多不赔的,还有很多对自己来说没什么用的保险,花了不少钱,还没买到自己想要的,殊不知只要多了解点汽车保险知识就能轻松省下一大笔钱并且买到自己真正想要的,何乐而不为呢! 因为卖保险的或者4S店,都是按照常规情况给你推荐保险,具体用车情况,只有你自己最清楚,所以保险是个个性化定制的产品,需要什么买什么,不需要的就没必要购买了。 一般