数组模拟二叉搜索树(二叉排序树)
文章目录
- 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 的前驱/后继数值
即查询二叉搜索树中,比 www 大的第一个数,和比 www 小的第一个数。
查找前驱数值
// 查找树中小于 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. 二叉排序树
题目描述
你需要写一种数据结构,来维护一些数,其中需要提供以下操作:
- 插入数值 xxx。
- 删除数值 xxx。
- 输出数值 xxx 的前驱(前驱定义为现有所有数中小于 xxx 的最大的数)。
- 输出数值 xxx 的后继(后继定义为现有所有数中大于 xxx 的最小的数)。
题目保证:
操作 111 插入的数值各不相同。
操作 222 删除的数值一定存在。
操作 333 和 444 的结果一定存在。
输入格式
第一行包含整数 nnn,表示共有 nnn 个操作命令。
接下来 nnn 行,每行包含两个整数 optoptopt 和 xxx,表示操作序号和操作数值。
输出格式
对于操作 3,43,43,4,每行输出一个操作结果。
数据范围
1≤n≤2000,1≤n≤2000,1≤n≤2000,
−10000≤x≤10000−10000≤x≤10000−10000≤x≤10000
输入样例:
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));}
}
数组模拟二叉搜索树(二叉排序树)相关推荐
- 将字符串转换为数组_LeetCode 树 108.将有序数组转换为二叉搜索树
7(108) 将有序数组转换为二叉搜索树 描述 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 ...
- 数据结构与算法--力扣108题将有序数组转换为二叉搜索树
力扣108提将有序数组转换为二叉搜索树 近一年都比较关注算法相关的知识,也刷了不少题,之前的文章中大多也是算法相关的文章,但是感觉每次遇到树相关的题型都不能应对自如,因此还是有必要在相关知识上下功夫, ...
- [leetcode-108,109] 将有序数组转换为二叉搜索树
109. 有序链表转换二叉搜索树 Given a singly linked list where elements are sorted in ascending order, convert it ...
- 20200703:将有序数组转换为二叉搜索树(leetcode108)
将有序数组转换为二叉搜索树 题目 思路与算法 代码实现 复杂度分析 题目 将有序数组转换为二叉搜索树 思路与算法 乍一看很简单,实际也很简单,今天的动态规划没做出来,就刷每日一题好了.保证平衡的最简单 ...
- 20190917:(leetcode习题)将有序数组转换为二叉搜索树
将有序数组转换为二叉搜索树 题目 大致思路 代码实现 题目 大致思路 将有序数组转换为二叉平衡树,即左边的数和右边数的差值不超过1,那么root节点应该为正中间的数,然后左边的剩余的中间为root.l ...
- laravel数组转换为字符串_LeetCode刷题实战108:将有序数组转换为二叉搜索树
算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...
- Suzy找到实习了吗 Day23 | 二叉树最后一节!669. 修剪二叉搜索树,108. 将有序数组转换为二叉搜索树,538. 把二叉搜索树转换为累加树
669. 修剪二叉搜索树 题目 给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high.通过修剪二叉搜索树,使得所有节点的值在[low, high]中.修剪树 不应该 改变保 ...
- LeetCode(Python)—— 将有序数组转换为二叉搜索树(简单)
将有序数组转换为二叉搜索树 概述:给你一个整数数组 nums ,其中元素已经按升序排列,请你将其转换为一棵高度平衡二叉搜索树.高度平衡二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 ...
- leetcode刷题记录--数据结构;深度优先搜索算法;二叉树;平衡树;1020. 飞地的数量;1669. 合并两个链表;108. 将有序数组转换为二叉搜索树
1020. 飞地的数量 难度中等131 给你一个大小为 m x n 的二进制矩阵 grid ,其中 0 表示一个海洋单元格.1 表示一个陆地单元格. 一次 移动 是指从一个陆地单元格走到另一个相邻(上 ...
最新文章
- cannot access a closed file
- 【Android 逆向】ELF 文件格式 ( ELF 文件当前版本号 | 操作系统 ABI 信息 | ABI 版本 | 文件头校验 | 文件头长度信息 )
- getHibernateTemplate()和getSession()的区别
- [linux]unixODBC的安装配置说明
- 封装案例-创建士兵类-完成初始化方法
- java string逆序_java经典入门算法题,java初学者必备
- centos7盘符 linux_linux_centos7_扩展磁盘空间
- 可口可乐开了家网店卖奶,价值62亿元
- 【Android】LMK 工作机制
- php为什么要创建类,php – 是否有理由为单一功能创建类?
- Javascript:流程控制
- 《Java语言程序设计与数据结构》编程练习答案(第十九章)(一)
- 黑苹果使用itlwm网卡驱动提示itlwm未运行的问题
- 10个Python爬虫入门实例
- python同构数_怎么用python判断一个数是否是同构数?
- Mac OS 使用笔记
- 定时跑视图往另外一张表添加数据_聊一聊数据库中的锁
- I Want To Spend My Lifetime Loving You
- 使用约会应用Tinder时没人关注你?花钱就能插队露脸半小时
- 计算机c盘可以删除的文件,电脑C盘上的文件哪些可以大胆删除的?
热门文章
- 学习系统编程No.15【深入动静态库】
- cgb2008-京淘day07
- C#中DllImport使用法汇总
- 冒泡法排序(PTA)
- UpdatePanel 控件简介
- loadlocale.c:130: _nl_intern_locale_data: Assertion `cnt (sizeof (_nl_value_type_LC_TIME) / sizeof
- 【Web开发】Python实现Web服务器(Bottle)
- JAVA源码学习要点汇总
- 微信小程序如何获取屏幕宽度
- phar反序列化小结