二叉树的具体原理及实现

文章目录

  • 一.树的专业术语
  • 二.二叉树的原理
  • 三.常见的二叉树分类
    • 1.完全二叉树
    • 2.平衡二叉树
    • 3.二叉搜索树
  • 四.二叉搜索树算法具体实现
  • 五.二叉搜索树具体实现代码

一.树的专业术语

首先先介绍树的专业术语
在这里插入图片描述

二.二叉树的原理

二叉搜索树(Binary Search Tree,BST)是一种常见的数据结构,它在计算机科学中被广泛应用于数据的存储和检索。它是一棵二叉树,其中每个节点都包含一个键值,并满足以下性质:

左子树中的所有节点的键值小于根节点的键值。
右子树中的所有节点的键值大于根节点的键值。
左子树和右子树也都是二叉搜索树。
这个性质使得二叉搜索树具有非常高效的查找、插入和删除操作。

二叉搜索树的原理可以通过以下几个操作来解释:

查找(Search):从根节点开始,比较要查找的键值与当前节点的键值。如果它等于当前节点的键值,则查找成功。如果要查找的键值小于当前节点的键值,则继续在左子树中查找;如果要查找的键值大于当前节点的键值,则继续在右子树中查找。直到找到匹配的键值或者遍历到叶子节点为止。

插入(Insertion):插入操作从根节点开始,比较要插入的键值与当前节点的键值。如果要插入的键值小于当前节点的键值,并且当前节点没有左子节点,则将新节点作为当前节点的左子节点;如果要插入的键值大于当前节点的键值,并且当前节点没有右子节点,则将新节点作为当前节点的右子节点。如果当前节点已有左子节点或右子节点,则继续在相应的子树上进行插入操作,直到找到合适的位置。

删除(Deletion):删除操作是比较复杂的,因为需要考虑不同的情况。首先,找到要删除的节点。如果要删除的节点没有子节点,可以直接删除它。如果要删除的节点只有一个子节点,可以用其子节点替换它。如果要删除的节点有两个子节点,可以找到其右子树中的最小节点(或者左子树中的最大节点)来替换它。替换后,再删除该最小(或最大)节点。删除操作需要保持二叉搜索树的性质。

总结来说,二叉搜索树通过利用节点键值的大小关系,将较小的值放在左子树,较大的值放在右子树。这样的组织结构可以在平均情况下以O(log n)的时间复杂度进行查找、插入和删除操作,但在最坏情况下,如果树的形状极度不平衡,时间复杂度可能会退化为O(n)。因此,在实际应用中,需要进行平衡操作,如红黑树或AVL树,以保证树的平衡性,提高性能。

三.常见的二叉树分类

1.完全二叉树

完全二叉树 — 若设二叉树的高度为 h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数第 h 层有叶子节点,并且叶子结点都是从左到右依次排布,这就是完全二叉树(堆就是完全二叉树)。

在这里插入图片描述
在这里插入图片描述

2.平衡二叉树

平衡二叉树— 又被称为 AVL 树,它是一颗空树或左右两个子树的高度差的绝对值不超过 1,并且左右两个子树都是一棵平衡二叉树。
在这里插入图片描述

3.二叉搜索树

二叉搜索树 — 又称二叉查找树、二叉排序树(Binary Sort Tree)。它是一颗空树或是满足下列性质的二叉树:
1.若左子树不空,则左子树上所有节点的值均小于或等于它的根节点的值;
2.若右子树不空,则右子树上所有节点的值均大于或等于它的根节点的值;
3.左、右子树也分别为二叉排序树。
在这里插入图片描述

四.二叉搜索树算法具体实现

当我们在数组中查找一个数的时候,需要从前往后逐个遍历,这样效率很忙
二叉搜索树就是把数据用它的规则进行从大到小排序,使用折半查找(二分查找)
在这里插入图片描述
二叉树一般采用链式存储方式:每个结点包含两个指针域,指向两个孩子结点,还包含一个数据域,存储结点信息。在这里插入图片描述
二叉搜索树插入节点
将要插入的结点 e,与节点 root 节点进行比较,若小于则去到左子树进行比较,若大于则去到右子树进行比较,重复以上
操作直到找到一个空位置用于放置该新节点

二叉搜索树删除节点
将要删除的节点的值,与节点 root 节点进行比较,若小于则去到左子树进行比较,若大于则去到右子树进行比较,重复以
上操作直到找到一个节点的值等于删除的值,则将此节点删除。删除时有 4 中情况须分别处理:
1.删除节点不存在左右子节点,即为叶子节点,直接删除
2.删除节点存在左子节点,不存在右子节点,直接把左子节点替代删除节点
3.删除节点存在右子节点,不存在左子节点,直接把右子节点替代删除节点
4.删除节点存在左右子节点,则取左子树上的最大节点或右子树上的最小节点替换删除节点。

二叉树的遍历
二叉树的遍历是指从根结点出发,按照某种次序依次访问所有结点,使得每个结点被当且访问一次。共分为四种方式:
前序遍历 - 先访问根节点,然后前序遍历左子树,再前序遍历右子树
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五.二叉搜索树具体实现代码

stack.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include "tree.h"#define MaxSize 128typedef struct _SqStack {Bnode* base;		//栈底指针Bnode* top;		//栈顶指针
}SqStack;bool InitStack(SqStack& S) //构造一个空栈 S
{S.base = new Bnode[MaxSize];//为顺序栈分配一个最大容量为 Maxsize 的空间if (!S.base) return false; //空间分配失败S.top = S.base; //top 初始为 base,空栈return true;
}bool PushStack(SqStack& S, Bnode e) {	 插入元素 e 为新的栈顶元素if (S.top - S.base == MaxSize) {printf("栈为满!\n");return false;}*(S.top++) = e;	//元素 e 压入栈顶,然后栈顶指针加 1,等价于*S.top=e;S.top++;return true;
}bool PopStack(SqStack& S, Bnode& e) {	//删除 S 的栈顶元素,暂存在变量 e中if (S.top == S.base) {printf("空栈!\n");return false;}e = *(--S.top);	//栈顶指针减 1,将栈顶元素赋给 ereturn true;
}Bnode* GetTop(SqStack& S) { //返回 S 的栈顶元素,栈顶指针不变if (S.base != S.top) {return S.top - 1;}else{printf("空栈!\n");return nullptr;}
}int GetSize(SqStack& S) {	//返回栈中元素个数return (S.top - S.base);
}bool IsEmpty(SqStack& S) {//判断栈是否为空if (S.top == S.base) {return true;}else {return false;}
}void DestroyStack(SqStack& S) {//销毁栈if (S.base) {free(S.base);S.base = NULL;S.top = NULL;}
}

tree.h

#pragma once
#define MAX_NODE 1024#define isLess(a,b)  (a<b)
#define isEqual(a,b) (a==b)typedef int ElemType;typedef struct _Bnode {ElemType data;	//数据struct _Bnode* lchild, * rchild;	//左右孩子节点
}Bnode, Btree;	//Bnode是结构体的指针类型,*Btree是提前定义好了的指向结构体的指针
//	*Btree等于提前创建了一个存储_Bnode类型的指针

main

#include <iostream>
#include <assert.h>
#include <Windows.h>
#include "tree.h"
#include "stack.h"#define MAX_NODE 1024using namespace std;bool InsertBtree(Btree** root, Bnode* node) {	 //插入Bnode* tmp = nullptr;Bnode* parent = nullptr;bool abs = false;if (!node) return false;else {node->lchild = nullptr;node->rchild = nullptr;}if (*root) {	//存在根节点tmp = *root;}else			//不存在根节点{*root = node;return true;}while (tmp != NULL){parent = tmp;	//保存父节点printf("父节点:%d\n", parent->data);if (isLess(node->data, tmp->data)) {tmp = tmp->lchild;abs = true;		//}else {tmp = tmp->rchild;abs = false;	}}//if (isLess(node->data,parent->data))if (abs) {	//找到空位置后,进行插入parent->lchild = node;}else{parent->rchild = node;}return true;
}int findMax(Btree* root) {assert(root != nullptr);//方式一,使用递归/*if (root->rchild) {root = root->rchild;}return root->data;*///方式二,使用循环while (root->rchild){root = root->rchild;}return root->data;
}Btree* DeleteNode(Btree* root, int key, Btree*& deleteNode) {if (root == nullptr)return NULL;	//没有找到删除的节点if (root->data > key) {root->lchild = DeleteNode(root->lchild, key, deleteNode);return root;}else if (root->data < key) {root->rchild = DeleteNode(root->rchild, key, deleteNode);return root;}deleteNode = root;//删除节点不存在左右子节点,即为叶子节点,直接删除//所有删除功能待实现if (root->lchild == nullptr && root->rchild == nullptr) return NULL;//删除节点只存在右子节点,直接用右子节点取代删除节点if (root->lchild == nullptr && root->rchild != nullptr)return root->rchild;//删除节点只存在左子节点,直接用左子节点取代删除节点if (root->lchild != NULL && root->rchild == NULL)return root->lchild;//删除节点存在左右子节点,直接用左子节点最大值取代删除节点//循环断点仔细看看这段代码int val = findMax(root->lchild);root->data = val;	//赋值root->lchild = DeleteNode(root->lchild, val, deleteNode);	//用完了就要删掉return root;
}//使用递归查询节点
Bnode* queryByRec(Btree* root, ElemType e) {if (root == nullptr || isEqual(root->data, e))return root;else if (isLess(e, root->data))return queryByRec(root->lchild, e);elsereturn queryByRec(root->rchild, e);
}//使用非递归查询节点
Bnode* queryByLoop(Bnode* root, int e) {while (root != nullptr && !isEqual(root->data, e)){if (isLess(root->data, e)) {root = root->rchild;}else {root = root->lchild;}}return root;
}//采用递归实现前序遍历
void PreOrderRec(Btree* root) {if (root == nullptr)return;printf("-%d-", root->data);PreOrderRec(root->lchild);PreOrderRec(root->rchild);
}//采用非递归实现前序遍历
//借助栈实现前序遍历
void PreOrder(Btree* root) {Bnode cur;if (root == nullptr)return;SqStack stack;InitStack(stack);PushStack(stack,*root);	//头节点先入栈while (!(IsEmpty(stack))){PopStack(stack, cur);	//要遍历的节点printf("-%d-", cur.data);if (cur.rchild != nullptr) {PushStack(stack,*(cur.rchild));	//右子节点先入栈,后处理}if (cur.lchild!=nullptr) {PushStack(stack, *(cur.lchild));//左子节点后入栈,接下来先处理}}DestroyStack(stack);
}int main(void) {int test[] = { 19, 7, 25, 5, 11, 15, 21, 61 };Bnode* root = NULL, * node = NULL;Bnode* Delete = nullptr;	//记录被删除的节点node = new Bnode;node->data = test[0];InsertBtree(&root, node);	//插入根节点for (int i = 1; i < sizeof(test) / sizeof(test[0]); i++) {node = new Bnode;node->data = test[i];if (InsertBtree(&root, node)) {printf("节点 %d 插入成功\n", node->data);}else {printf("节点 %d 插入失败\n", node->data);}}Bnode* tmp = queryByRec(root, 25);printf("搜索二叉搜索树,节点 25 %s\n", tmp ? "存在" : "不存在");Bnode* tmp1 = queryByRec(root, 55);printf("搜索二叉搜索树,节点 55 %s\n", tmp1 ? "存在" : "不存在");cout << endl;PreOrderRec(root);cout << endl;PreOrder(root);cout << endl;cout << "删除节点 25" << endl;Bnode* del = DeleteNode(root, 25, Delete);Bnode* tmp2 = queryByRec(root, 25);delete Delete;	//销毁内存printf("搜索二叉搜索树,节点 25 %s\n", tmp2 ? "存在" : "不存在");cout << endl;PreOrderRec(root);cout << endl;PreOrder(root);cout << endl;system("pause");return 0;
}

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

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

相关文章

【rar密码】WinRAR整理密码,如何使用?

之前给大家介绍过WinRAR自动加密的设置方法&#xff0c;今天再介绍一种RAR压缩包加密方法&#xff1a;整理密码。 什么是整理密码&#xff1a; 在加密rar文件的时候&#xff0c;点击下拉框选择密码&#xff0c;不用输入密码 设置方法&#xff1a; 前面的操作步骤和设置自动…

在ExoPlayer中使用协程:构建强大的Android媒体播放器

在ExoPlayer中使用协程&#xff1a;构建强大的Android媒体播放器 现今的移动应用世界中&#xff0c;媒体消费是用户体验的核心部分。无论是流媒体视频、音乐播放还是处理自适应媒体格式&#xff0c;强大的媒体播放器对于提供无缝和愉悦的用户体验至关重要。而在安卓平台上&…

Python(PySpark案例实战)

为什么要学习PySpark&#xff1f; Spark对Python语言的支持&#xff0c;重点体现在&#xff0c;Python第三方库&#xff1a;PySpark之上。 PySpark是由Spark官方开发的Python语言第三方库。 Python开发者可以使用pip程序快速的安装PySpark并像其它三方库那样直接使用。 PySp…

VUE之proxy配置实现跨域

什么是跨域 要了解跨域&#xff0c;首先得知道浏览器的同源策略。 同源策略&#xff1a;是由Netscape提出的一个安全策略&#xff0c;能够阻挡恶意文档&#xff0c;保护本地数据。它能限制一个源的文档或脚本对另一个源的交互&#xff0c;使得其它源的文档或脚本&#xff0c;…

SQL Server 日期范围按每月一行拆分

要将 SQL Server 中的日期范围按每月一行拆分&#xff0c;可以使用一个表值函数&#xff08;Table-Valued Function&#xff09;来生成日期范围内的月份&#xff0c;并将其与其他数据连接&#xff0c;以创建包含每月一行的结果集。 以下是一个示例&#xff0c;说明如何实现这一…

c++ decltype()的两个特殊情况

我们对里面这一行反汇编&#xff1a; 很诡异的左值引用。提供了这么违反感觉的语法&#xff0c;可能是为了语法完备性&#xff0c;但不直观。

【基本数据结构 三】线性数据结构:栈

学习了数组和链表后,再来看看第三种线性表结构,也就是栈,栈和后边讲的队列一样是一种受限的线性表结构,正是因为其使用有限制,所以对于一些特定的需要操作可控的场合,受限的结构就非常有用。 栈的定义 我们平时放盘子的时候,都是从下往上一个一个放;取的时候,我们也…

Java8中判断一个对象不为空存在一个类对象是哪个

Java8中判断一个对象不为空存在一个类对象是哪个&#xff1f; 在Java 8中&#xff0c;你可以使用java.util.Optional类来处理可能为空的对象。Optional类可以帮助你优雅地处理空值情况&#xff0c;而不需要显式地进行空值检查。 这是一个简单的Optional示例&#xff1a; imp…

Matlab中关于 : 的使用

设&#xff0c;mat 这个矩阵的规格是 n*m&#xff0c;temp mat( i , j ) 矩阵的行和列的下标从1开始 在这个矩阵中&#xff0c;a:b 代表的含义是范围是从 a--b 则&#xff0c;当 a 和 b 被省略时&#xff0c;代表的范围就是最大范围&#xff08;1--n&#xff09; or &#…

竞赛选题 基于深度学习的人脸性别年龄识别 - 图像识别 opencv

文章目录 0 前言1 课题描述2 实现效果3 算法实现原理3.1 数据集3.2 深度学习识别算法3.3 特征提取主干网络3.4 总体实现流程 4 具体实现4.1 预训练数据格式4.2 部分实现代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 毕业设计…

通过RSYNC在linux和windows间同步文件

通过RSYNC在linux和windows间同步文件 下载windows版本rsync下载后是一个zip的压缩包&#xff0c;直接解压就可使用配置windows到linux的秘钥拷贝公钥文件到linux服务器&#xff0c;实现免密配置同步命令结合windows计划任务实现定时同步文件 下载windows版本rsync 下载链接 h…

后端中间件安装与启动(Redis、Nginx、Nacos、Kafka)

后端中间件安装与启动 RedisNginxNacosKafka Redis 1.打开cmd终端&#xff0c;进入redis文件目录 2.输入redis-server.exe redis.windows.conf即可启动&#xff0c;不能关闭cmd窗口 &#xff08;端口配置方式&#xff1a;redis目录下的redis.windows.conf配置文件&#xff0c;…