数据结构与算法之树(二)二叉查找树

article/2025/8/19 6:22:21

数据结构与算法之树

数据结构与算法之树(一)二叉树概念及遍历方式(图文并茂)

数据结构与算法之树(二)二叉查找树

数据结构与算法之树(三)AVL树

数据结构与算法之树(四)红黑树

数据结构与算法之树(二)二叉查找树

文章目录

  • 数据结构与算法之树(二)二叉查找树
    • 一、二叉查找树结构
    • 二、二叉查找树查找
    • 三、二叉查找树插入
    • 四、二叉查找树删除
    • 五、二叉查找树的不足

一、二叉查找树结构

二叉查找树的二叉树的一种常见类型,又可以称为二叉搜索树。顾名思义,二叉查找树支持快速的查找,不仅如此,它还支持快速的插入与删除,二叉查找树是如何实现这些特性的呢?

这就需要看二叉查找树的结构,二叉查找树要求任意一个节点,其左子树的每个节点的值要小于此节点,右子树中每个节点的值要大于此节点,如下图就是一棵二叉查找树

在这里插入图片描述

可以看到,每一个节点的左子树的节点的值都小于该节点,右子树的节点的值都大于该节点

了解了二叉查找树的结构后,关于二叉查找树如何查找、插入、删除下面将一一讨论

二、二叉查找树查找

二叉搜索树的查找是采用二分的思想

查找方法如下

首先从根节点开始,如果要查找的值等于该节点的值,那么就返回。否则,如果要查找的值小于该节点的值,那么就往左节点跳,如果要查找的值大于该节点的值,那么就往右节点跳

下面以我们在以下这棵二叉查找树中,来查找5这个值

在这里插入图片描述

查找步骤

①我们从根节点开始查找

②5比7小,那么往左节点跳

③5比4大,往右节点跳

④5比6小,往左节点跳,此时找到值等于5的节点,查找成功返回

在这里插入图片描述

查找的程序如下

#include <stdio.h>
#include <stdlib.h>struct Node
{int value;Node* left;Node* right;
};struct Node* root;struct Node* binarySearchTreeFind(struct Node** root, int value)
{struct Node* node = *root;while(node != NULL){if(value < node->value)node = node->left;else if(value > node->value)node = node->right;elsereturn node;}return NULL;
}

三、二叉查找树插入

二叉查找树的插入和查找做法十分类似,都是采用二分的思想

插入方法如下

首先从根节点开始,如果插入的值小于该节点的值,那么就往左跳,否则就往右跳,直到找到空位置,这里就是插入节点

下面考虑在这棵二叉搜索树中,插入2这个数的整一个过程

在这里插入图片描述

插入步骤

①从根节点开始

②发现2比7小,那么往左节点跳

③2比4小,继续往左节点跳

④2比1大,此时发现1的右孩子为空,那么就找到了插入位置,在此处插入值为2的节点

在这里插入图片描述

你可能会发现,我们并没有将等于作为一种特殊情况,而是将其归为大于等于,这是因为我们允许插入值相同的节点,继续上面的二叉搜索树为例,如果插入4这个已存在的值,最后会插入到哪里

在这里插入图片描述

插入的程序如下

struct Node* createNode(int value)
{struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));newNode->value = value;newNode->left = NULL;newNode->right = NULL;return newNode;
}void binarySearchTreeInsert(struct Node** root, int value)
{struct Node* node = *root;/* 树根为空,为其创建一个节点 */if(node == NULL){*root = createNode(value);return;}struct Node* pNode; //保留父节点while(node != NULL){pNode = node;if(value < node->value)node = node->left;elsenode = node->right;}/* 插入新节点 */if(value < pNode->value)pNode->left = createNode(value);elsepNode->right = createNode(value);
}

四、二叉查找树删除

相对于查找和插入,删除就显得复杂一点了

删除方法

总的可以分为下面三种情况

1.如果删除节点没有左右子节点,我们只要将其父节点指向该节点的指针设置为空,然后删除该节点接口

2.如果删除节点有一个子节点,那么我们将其父节点指向删除节点的指针指向删除节点的子节点,然后删除该节点

3.如果删除节点有左右两个子节点,这种情况稍微复杂,我们需要将其右子树最左的节点来替换该节点,然后删除该节点

对于上面这几种情况可能会稍微复杂,下面我使用图示你就明白了

首先有请我们要操作的二叉查找树出场

在这里插入图片描述

情况1

删除节点没有子节点

我们以值为9的这个节点为例,将其删除,如下

在这里插入图片描述

情况2

删除节点有一个子节点

我们以值为5的这个节点为例,将其删除,如下

在这里插入图片描述

情况3

删除节点有左右两个子节点

这种情况是比较复杂的,首先需要找到删除节点右子树最左的节点,将其替换删除节点,如下

我们以值为3的这个节点为例,将其删除,如下

在这里插入图片描述

为什么需要找到右子树的最左节点呢?

我们想一下,在删除3节点的时候,肯定需要找一个新节点来替代它,那么此时是1或者6节点,那么就会破坏二叉查找树的规则

我们需要找的是,大于删除节点的左子树,小于删除节点的右子树,为了大于删除节点的左子树,那么就需要在删除节点的右子树查找,为了小于删除节点的右子树,那么就必须是删除节点右子树最左边的节点

删除指定节点的程序如下

void binarySearchTreeDelete(struct Node** root, int value)
{struct Node* node = *root;struct Node* pNode = NULL; //保存父节点struct Node* curNode = NULL;/* 查找要删除的节点 */while(node != NULL){if(value < node->value){pNode = node;node = node->left;}else if(value < node->value){pNode = node;node = node->right;}elsebreak;}/* 没有找到 */if(node == NULL)return;if(node->left == NULL && node->right == NULL) //case 1{if(pNode)  //删除节点不是根节点value < pNode->value ? pNode->left = NULL : pNode->right = NULL;else //删除节点为根节点curNode = NULL;free(node);}else if(node->left != NULL && node->right != NULL) //case 3{struct Node* minNode = node->right; //右子树最左节点struct Node* pMinNode = node; //右子树最左节点的父节点while(minNode->left != NULL){pMinNode = minNode;minNode = minNode->left;}/* 替换要删除的节点 */node->value = minNode->value;if(pMinNode == node) //右子树没有左节点pMinNode->right = minNode->right;else //找到了右子树的最左节点pMinNode->left = NULL;free(minNode);/* 如果删除的是根节点 */if(!pNode)curNode = node;}else //case 2{if(node->left != NULL) //左子节点不为空{if(pNode) //删除节点不是根节点value < pNode->value ? pNode->left = node->left : pNode->right = node->left;else //删除节点为根节点curNode = node->left;free(node);}else //右子节点不为空{if(pNode) //删除节点不是根节点value < pNode->value ? pNode->left = node->right : pNode->right = node->right;else //删除节点为根节点curNode = node->right;free(node);}}/* 如果删除的是根节点 */if(!pNode)*root = curNode;
}

可以看到删除的代码还是比较复杂的

你可以将上面的几段代码拷贝下来,运行下面的测试程序

int main(int argc, char* argv[])
{struct Node* node;int value;int i;for(i = 0; i < 50000; ++i){value = rand() % 50000;binarySearchTreeInsert(&root, value);}for(i = 50; i < 70; ++i){node = binarySearchTreeFind(&root, i);if(node)printf("find %d\n", node->value);}for(i = 10000; i < 10020; ++i){node = binarySearchTreeFind(&root, i);if(node)printf("find %d\n", node->value);}for(i = 500; i < 550; ++i)binarySearchTreeDelete(&root, i);return 0;
}

你会发现插入和查找操作都非常的快

五、二叉查找树的不足

到此你应该对二叉搜索树非常熟悉了,下面来看看二叉搜索树的时间复杂度

假设插入的数非常的随机,那么这棵二叉树搜索树会非常的平衡,假设节点数的n,那么此时数的高度接近于logn,而二叉查找树查找最坏的情况也只是遍历树的高度,所以此时的时间复杂度为Ologn

但是,如果插入的树非常的极端,一个比一个大,那么二叉搜索树在极端情况下会变成链表,此时时间复杂为On

在这里插入图片描述

为了解决这个问题,于是发明了平衡二叉树,具体的平衡二叉树有AVL树、红黑树等,下一篇文章将进一步讲解

OK,本文到此结束


http://chatgpt.dhexx.cn/article/50dcPYU8.shtml

相关文章

【算法修炼】二叉搜索树

学习自&#xff1a;https://labuladong.gitee.io/algo/2/19/26/ 二叉搜索树 一、BST的中序遍历230、二叉搜索树中第k小的元素&#xff08;中等&#xff09;1038、从二叉搜索树到更大和树&#xff08;中等&#xff09; 二、判断BST的合法性※98、验证二叉搜索树&#xff08;中等…

数据结构(C语言)-树

树 一、树1、树的定义2、树的基本术语3、树结构和线性结构的比较 二、二叉树1、二叉树的定义2、二叉树的形态与树的形态3、二叉树的性质4 、二叉树的存储结构5、遍历二叉树6、二叉树的其他操作7、线索二叉树 三、树与二叉树的转换1、树转换成二叉树2、二叉树变树 四、哈夫曼树1…

【数据结构与算法】程序内功篇六--树

程序内功篇六--树 一、树1、树的含义2、树的特点(选看)3、树的逻辑结构 二、二叉树1、二叉树的含义2、二叉树性质3、二叉树-顺序存储4、二叉树-链式存储5、二叉树的遍历6、二叉树创建与遍历C程序的实现&#xff08;1&#xff09;二叉树的创建&#xff08;2&#xff09;前序遍历…

数据结构---与树相关的知识

与树有关的一系列知识: 树,二叉树,满二叉树,完全二叉树,文章还没完,我会后序补充的 一: 树(了解就行)1.1 概念1.2 一些与树相关的重要概念1.3 树的表示形式 二: 二叉树(非常重要,重点掌握)2.1 概念2.2 两种特殊的二叉树2.2.1 满二叉树2.2.2 完全二叉树 2.3 二叉树的性质2.4 二叉…

Java数据结构--树1

Java数据结构--树 一、二叉树入门1.1 树的基本定义1.2 树的相关术语1.3 二叉树的基本定义1.4 二叉查找树的创建1.4.1 二叉树的结点类1.4.2 二叉查找树API设计1.4.3 二叉查找树实现1.4.4 二叉查找树其他便捷方法1.4.4.1 查找二叉树中最小的键1.4.4.2 查找二叉树中最大的键 1.5 二…

树 一

文章目录 1.查找二分查找判定树 2. 树(Tree)2.1 树的术语2.2树的表示&#xff1a;儿子兄弟表示法 3. 二叉树&#xff08;Binary Tree&#xff09;3.1 特殊结构二叉树3.2 二叉树的性质3.3 二叉树的存储3.4二叉树的遍历 分层次组织管理上更有效地操作。 1.查找 静态查找&#xf…

树一:邂逅入门篇

一、树的概念 树是一种典型的非线性结构&#xff0c;是表达有层次特性的图结构的一种方法。 1.1 基本术语 术语描述空树当n0 时称为空树。根结点根结点是一个没有双亲结点的结点。一棵树最多一个。例如&#xff1a;A边结点之间的连线叶子结点没有孩子结点的结点。例如&#x…

树一:定义及存储

树的定义&#xff1a; 树是一种非线性的数据结构。树是由 n (n > 0) 个结点组成的有序集合。 如果 n 为0&#xff0c;称为空树&#xff1b;如果 n > 0, 则&#xff1a; 有一个结点称为根结点&#xff08;root&#xff09;&#xff0c;它有直接后继&#xff0c;但没有直接…

mysql 创建索引规则

1、表的主键、外键必须有索引&#xff1b; 2、数据量超过300的表应该有索引&#xff1b; 3、经常与其他表进行连接的表&#xff0c;在连接字段上应该建立索引&#xff1b; 4、经常出现在Where子句中的字段&#xff0c;特别是大表的字段&#xff0c;应该建立索引&#xff1b; 5、…

mysql分区表之三:MySQL分区建索引,唯一索引

介绍 mysql分区后每个分区成了独立的文件&#xff0c;虽然从逻辑上还是一张表其实已经分成了多张独立的表&#xff0c;从“information_schema.INNODB_SYS_TABLES”系统表可以看到每个分区都存在独立的TABLE_ID,由于Innodb数据和索引都是保存在".ibd"文件当中&#…

分享mysql创建索引的3种方法

大家应该都知道索引的建立对于MySQL数据库的高效运行是很重要的,索引可以大大提升MySQL的检索速度,下面这篇文章主要给大家介绍了关于mysql创建索引的3种方法,需要的朋友可以参考下 1、使用CREATE INDEX创建&#xff0c;语法如下&#xff1a; 1 CREATE INDEX indexName ON tab…

js 监听浏览器刷新还是关闭事件

// $(window).bind(beforeunload,function(){return 您输入的内容尚未保存&#xff0c;确定离开此页面吗&#xff1f;;}); // window.onbeforeunload function() { return "确定离开此页面吗&#xff1f;"; }; // function myFunction() {return "自定…

浏览器刷新和页面手动为什么不一样?

Fiddler(2):AutoResponse修改返回结果_mb5fed6ec4336ce的技术博客_51CTO博客Fiddler(2):AutoResponse修改返回结果&#xff0c;前言怎么修改接口的返回数据呢步骤1.抓包&#xff0c;找到要拦截的请求&#xff0c;然后在AutoResponder中AddRule&#xff1a;2.在RuleEditor中的第…

vue监听浏览器刷新和关闭事件,并在页面关闭/刷新前发送请求

vue监听浏览器刷新和关闭事件&#xff0c;并在页面关闭/刷新前发送请求 1.需求背景&#xff1a;2.需求分析&#xff1a;3.实现方式&#xff1a;4.实现方式解析&#xff1a;1&#xff09;浏览器页面事件基础2&#xff09;在mounted监听beforeunload和unload事件 5.存在的问题/风…

浏览器刷新和关闭时显示提示信息

vue 刷新和关闭浏览器时显示提示信息 使用onbeforeunload事件 mounted() {window.onbeforeunload e > {e e || window.eventif (e) {e.returnValue 关闭提示}return 关闭提示}} }, beforeDestroy() {window.onbeforeunload null },如果是所有页面都需要页面销毁显示提…

【Vue实用功能】Vue监听浏览器刷新和关闭事件

Vue监听浏览器刷新和关闭事件 在前端开发中&#xff0c;我们通常会遇到这样的需求&#xff0c;用户离开、刷新页面前&#xff0c;修改数据未进行保存操作&#xff0c;需要提示框提醒用户 效果实现 methods: {/** 在刷新和关闭之前询问 **/beforeRefreshClose() {let self t…

vue监听浏览器刷新和关闭;

注意&#xff1a;区分不了浏览器是触发了刷新还是关闭&#xff0c;而且提示的弹框是无法自定义的&#xff1b;如果有大佬有方法能区分&#xff0c;还请评论学习一下&#xff01;感谢&#xff01; 代码可直接复制&#xff1a; <template><div><div /></di…

JS阻止浏览器刷新的方法

直接先给朋友们上阻止浏览器刷新的代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv&quo…

VSCODE同步浏览器刷新

VSCODE同步浏览器刷新 安装插件 live server

java中foreach的用法

文章目录 前言语法用法用法1&#xff1a;输出一维数组用法2&#xff1a;输出二维数组foreach的局限性什么是索引 总结 前言 java中foreach,可以认为是增强版的for语句循环&#xff0c;它可以减少代码量&#xff0c;但是不是所有的foreach都可以代替for循环。 语法 foreach的…