数组模拟二叉搜索树(二叉排序树)

article/2025/8/25 0:10:39

文章目录

  • 1. 二叉搜索树的定义
  • 2. 二叉搜索树经典模板
    • 2.1 插入操作(建树操作)
    • 2.2 删除操作
    • 2.3 查询二叉搜索树中值为 w 的前驱/后继数值
  • 3. 经典例题

1. 二叉搜索树的定义

二叉搜索树 (BST) 递归定义为具有以下属性的二叉树:

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
  • 若它的右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值
  • 它的左、右子树也分别为二叉搜索树

二叉搜索树有一个特殊的性质:二叉搜索树的中序遍历是一个有序序列

2. 二叉搜索树经典模板

这里的二叉搜索树的构建是基于一个序列元素的依次插入。

2.1 插入操作(建树操作)

二叉搜索树的插入操作时间复杂度是 O ( h ) O(h) O(h)

#include <iostream>
#include <cstring>
using namespace std;const int N = 2010, INF = 1e8;
// idx 代表下一个可以使用的空白结点,其中 0 代表空状态,第1个结点位置应该是从1开始
int l[N], r[N], v[N], idx;// 最核心的插入操作
void insert(int& u, int w)
{if (!u){u = ++idx; // 类似于Trie字典树,0作为空状态不用(Trie中0代表根节点)v[u] = w;}else if (w < v[u]]) insert(l[u], w);else insert(r[u], w);
}int main()
{int n;scanf("%d", &n); // 要插入结点的个数int root = 0;for (int i = 0; i < n; i++){int x;scanf("%d", &x);insert(root, x); // 依次插入到二叉搜索树中}
}

2.2 删除操作

删除结点操作需要分三种情况讨论:

(1)若删除的结点是叶结点:直接删除;

(2)若删除的结点仅有一个子树:直接将子树根节点替换到删除结点的位置上;
【解释】:直接用儿子结点替换删除结点是不会影响中序遍历有序性的。假设待删除结点只有左子树,则左子树上所有结点都是小于删除结点、大于或等于待删除结点的父结点的,因此用左子树的根替换待删除结点并不会破坏整棵树中序遍历的有序性

(3)若删除的结点同时拥有左右子树:则将待删除结点的前驱结点(或后继结点)的值直接覆盖到待删除结点上,然后删除前驱结点(或后继结点)。这里的前驱结点是指“待删除结点的左子树中最大结点”,后继结点指的是“待删除结点右子树中最小结点”。
【解释】:如果放在二叉搜索树中序遍历序列中,可以理解为下图:
在这里插入图片描述
二叉搜索树的删除操作时间复杂度是 O ( h ) O(h) O(h)

// 删除操作
void remove(int& u, int w)
{if (!u) return; // 如果找不到要删除的值,直接结束if (w < v[u]) remove(l[u], w);else if (w > v[u]) remove(r[u], w);else{if (!l[u] && !r[u]) u = 0; // 如果是叶子结点,直接删除else if (!r[u]) u = l[u]; // 如果右子树为空,则左子树替换else if (!l[u]) u = r[u]; // 如果左子树为空,则右子树替换else{int p = l[u];// 找到左子树的最大值while (r[p]) p = r[p];v[u] = v[p]; // 直接将值进行覆盖,注意不是替换remove(l[u], v[p]); // 然后去左子树中删掉前驱}}
}

2.3 查询二叉搜索树中值为 w 的前驱/后继数值

即查询二叉搜索树中,比 w w w 大的第一个数,和比 w w w 小的第一个数。

查找前驱数值

// 查找树中小于 w 的最大值
void get_pre(int u, int w)
{if (!u) return -INF; // 说明没找到if (v[u] >= w) return get_pre(l[u], w);return max(v[u], get_pre(r[u], w));
}

查找后继数值

// 查找树中大于 w 的最小值
void get_post(int u, int w)
{if (!u) return INF;if (v[u] <= w) return get_post(r[u], w);return min(v[u], get_post(l[u], w));
}

3. 经典例题

题目来源:AcWing 3786. 二叉排序树

题目描述
你需要写一种数据结构,来维护一些数,其中需要提供以下操作:

  1. 插入数值 x x x
  2. 删除数值 x x x
  3. 输出数值 x x x 的前驱(前驱定义为现有所有数中小于 x x x 的最大的数)。
  4. 输出数值 x x x 的后继(后继定义为现有所有数中大于 x x x 的最小的数)。

题目保证:

操作 1 1 1 插入的数值各不相同。
操作 2 2 2 删除的数值一定存在。
操作 3 3 3 4 4 4 的结果一定存在。

输入格式
第一行包含整数 n n n,表示共有 n n n 个操作命令。

接下来 n n n 行,每行包含两个整数 o p t opt opt x x x,表示操作序号和操作数值。

输出格式
对于操作 3 , 4 3,4 3,4,每行输出一个操作结果。

数据范围
1 ≤ n ≤ 2000 , 1≤n≤2000, 1n2000
− 10000 ≤ x ≤ 10000 −10000≤x≤10000 10000x10000

输入样例:

6
1 1
1 3
1 5
3 4
2 3
4 2

输出样例:

3
5

AC代码如下

#include <iostream>
#include <cstring>
using namespace std;const int N = 2010, INF = 1e8;
int l[N], r[N], v[N], idx;
int n;// 二叉搜索树的插入
void insert(int& u, int w)
{if (!u) {u = ++idx;v[u] = w;}else if (w < v[u]) insert(l[u], w);else insert(r[u], w);
}// 二叉搜索树的删除操作
void remove(int& u, int w)
{if (!u) return; // 找不到wif (w < v[u]) remove(l[u], w);else if (w > v[u]) remove(r[u], w);else{if (!l[u] && !r[u]) u = 0;else if (!r[u]) u = l[u];else if (!l[u]) u = r[u];else{int p = l[u];while (r[p]) p = r[p];v[u] = v[p];remove(l[u], v[p]);}}
}// 查询树中比w小的最大的数
int get_pre(int u, int w)
{if (!u) return -INF;if (v[u] >= w) return get_pre(l[u], w);return max(v[u], get_pre(r[u], w));
}// 查询树中比w大的最小的数
int get_post(int u, int w)
{if (!u) return INF;if (v[u] <= w) return get_post(r[u], w);return min(v[u], get_post(l[u], w));
}int main()
{scanf("%d", &n);int root = 0;while (n--){int t, x;scanf("%d%d", &t, &x);if (t == 1) insert(root, x);else if (t == 2) remove(root, x);else if (t == 3) printf("%d\n", get_pre(root, x));else printf("%d\n", get_post(root, x));}
}

http://chatgpt.dhexx.cn/article/8LIMyh3a.shtml

相关文章

是否二叉搜索树

6-21 是否二叉搜索树 &#xff08;25 分&#xff09; 本题要求实现函数&#xff0c;判断给定二叉树是否二叉搜索树。 函数接口定义&#xff1a; bool IsBST ( BinTree T );其中BinTree结构定义如下&#xff1a; typedef struct TNode *Position; typedef Position BinTree;…

数据结构-二叉搜索树

目录 二叉搜索树的概念及结构 概念 结构 二叉搜索树的基本操作 默认成员函数 默认构造函数与拷贝构造函数 赋值重载 析构函数 二叉搜索树的插入 非递归 递归 二叉搜索树的遍历 中序遍历---升序&#xff08;左根右&#xff09; 中序遍历---降序&#xff08;右根左…

二叉搜索树的应用

文章目录 1. 二叉搜索树的应用2. 二叉搜索树的KV模型3. 关于const K导致无法erase4. 关于while (cin >> str)5. 面试oj题5.1根据二叉树创建字符串5.2二叉树的层序遍历5.3二叉树的最近公共祖先5.4 二叉搜索树与双向链表5.5从前序与中序遍历序列构造二叉树5.6二叉树的前序遍…

【C++】二叉搜索树

目录 一、二叉搜索树概念 1、概念 2、结构 3、性质 二、二叉搜索树模拟实现 1、二叉搜索树节点 2、二叉搜索树构造函数 3、二叉搜索树查找 (1)迭代版本 (2)递归版本 4、二叉搜索树插入 (1)迭代版本 (2)递归版本 5、二叉搜索树节点删除 (1)迭代版本 (2)递归版本 …

Python 二叉搜索树

二叉搜索树(BST)&#xff0c;有时也被称为二叉排序树&#xff0c;是一种特殊类型的容器: 在内存中存储“项目”&#xff08;例如数字&#xff0c;名称等&#xff09;的数据结构。它们允许快速查找、添加和删除项目。二叉排序树是带根结点的二叉树&#xff0c;其内部结点各自存储…

构建二叉搜索树

二叉搜索树 (BST) 递归定义为具有以下属性的二叉树&#xff1a; 若它的左子树不空&#xff0c;则左子树上所有结点的值均小于它的根结点的值若它的右子树不空&#xff0c;则右子树上所有结点的值均大于或等于它的根结点的值它的左、右子树也分别为二叉搜索树 给定二叉树的具体…

【Java 数据结构】实现一个二叉搜索树

目录 1、认识二叉搜索树 2、实现一个二叉搜索树 2.1 成员变量 2.2 insert 方法 2.3 search 方法 2.4 remove 方法(重点) 3、二叉搜索树总结 1、认识二叉搜索树 从字面上来看&#xff0c;它只比二叉树多了搜索两个字&#xff0c;我们回想一下&#xff0c;如果要是在二…

java二叉搜索树详解

文章目录 一、概念二、相关操作2.0节点相关代码&#xff1a;2.1查找2.2插入2.3删除&#xff08;重难点&#xff09;2.4测试用例 三、小结 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、概念 二叉搜索树又称二叉排序树 它或者是一棵空树&#xff…

二叉搜索树

目录 二叉搜索树二叉搜索树概念 增删查改接口插入递归插入查找递归查找删除递归删除 成员函数拷贝构造拷贝赋值析构 二叉搜索树的应用二叉搜索树的性能分析 二叉搜索树 二叉搜索树概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的…

【数据结构】二叉搜索树

目录 一.二叉搜索树规则 二.框架 三.实现 1.查找, 插入, 删除 迭代法实现 递归法实现 2.构造, 赋值 默认构造 拷贝构造 赋值运算符重载 3.中序遍历 四.时间复杂度及稳定性 五.key模型与key/value模型 1.key模型 2.key/value模型 一.二叉搜索树规则 二叉搜索树又…

数据结构——二叉搜索树详解

二叉搜索树 一、什么是二叉搜索树 二叉搜索树&#xff08;BST&#xff0c;Binary Search Tree&#xff09;&#xff0c;也称二叉排序树或二叉查找树。 二叉搜索树&#xff1a;一棵二叉树&#xff0c;可以为空&#xff1b;如果不为空&#xff0c;满足以下性质&#xff1a; 非空…

【数据结构】——二叉搜索树

目录 前言 二叉搜索树的概念 二叉搜索树的操作 树的节点实现 搜索树的基本结构 插入数据 查找 删除 拷贝构造函数 二叉搜索树的应用 前言 在c中的容器里map和set的学习需要二叉搜索树的铺垫&#xff0c;也为后边的的红黑树和AVL树做铺垫&#xff0c;也就是说&#xff0c;…

二叉搜索树(二叉排序树)

一.概念 二叉搜索树又称二叉排序树&#xff0c;具有以下性质&#xff1a; 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值它的左右子树也分别为二叉搜索树 注意&#xff1a;二…

二叉排序树(二叉查找树、二叉搜索树)(图解+完整代码)

目录 ⚽1.什么是二叉排序树 &#x1f3d0;2.构建二叉排序树 &#x1f3c0;3.二叉排序树的查找操作 &#x1f94e;4.二叉排序树的删除 &#x1f3b1;5.完整代码 ⚽1.什么是二叉排序树 我们直接看它的性质&#xff1a; 若它的左子树不空&#xff0c;则左子树上所有结点的值…

种树:二叉树、二叉搜索树、AVL树、红黑树、哈夫曼树、B树、树与森林

虽然今天不是植树节&#xff0c;但是我今天想种树。 文章目录 树&#xff0c;什么是树&#xff1f;二叉树定义二叉树的创建二叉树的前中后序遍历前序遍历&#xff1a;中序遍历后序遍历已知前序、中序遍历结果&#xff0c;还原二叉树已知后序、中序遍历结果&#xff0c;还原二叉…

java lrucache_Java LruCache 的使用及原理

概述 LRU (Least Recently Used) 的意思就是近期最少使用算法&#xff0c;它的核心思想就是会优先淘汰那些近期最少使用的缓存对象。 在我们日常开发中&#xff0c;UI 界面进行网络图片加载是很正常的一件事情&#xff0c;但是当界面上的图片过于多的时候&#xff0c;不可能每次…

LruCache和DiskLruCache

前言 Android中的三级缓存主要就是内存缓存和硬盘缓存。 Lru(least recently used)意为最近最少使用算法&#xff0c;核心思想就是当缓存满时&#xff0c;会优先淘汰最近最少使用的缓存对象。 LruCache的使用 在Android中可以直接使用LruCache&#xff0c;算法原理是&#xff1…

Android LruCache 缓存

使用场景 1、场景一&#xff1a;图片缓存利器。 可以规定缓存大小、有效避免OOM、自动移除队尾不用的图片缓存、避免HashMap各种问题。 2、场景二&#xff1a;通信缓存 从服务端需要获取数据&#xff0c;但是当访问的数据比较大&#xff0c;比较多&#xff0c;并且是重复数…

Java——LRUCache

概念 简单来说&#xff0c;由于我们的空间是有限的&#xff0c;所以发明了这个数据结构&#xff0c;当我们的空间不够添加新的元素时&#xff0c;就会删除最近最少使用的元素。 其底层逻辑通过哈希表和链表共同实现。哈希表中存储链表的每一个元素&#xff0c;方便进行元素的…

LruCache缓存

Lru算法&#xff1a; Lru 指的是“Least Recently Used-近期最少使用算法”。 1、那么LruCache到底是什么呢&#xff1f; LruCache 是对限定数量的缓存对象持有强引用的缓存&#xff0c;每一次缓存对象被访问&#xff0c;都会被移动到队列的头部。当有对象要被添加到已经达到数…