44 二叉搜索树中第K个小的元素

二叉搜索树中第K个小的元素

    • 题解1 中序遍历
    • 题解2 AVL(手撕平衡二叉树:谢谢力扣官方)

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。

在这里插入图片描述
提示:

  • 树中的节点数为 n 。
  • 1 <= k <= n <= 1 0 4 10^4 104
  • 0 <= Node.val <= 1 0 4 10^4 104

进阶:如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化算法?

题解1 中序遍历

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:int kthSmallest(TreeNode* root, int k) {vector<int> vals;stack<TreeNode*> kk;while(vals.size() < k){while(root){kk.push(root);root = root->left;}root = kk.top();kk.pop();vals.emplace_back(root->val);root = root->right;}return vals.back();}
};

在这里插入图片描述

题解2 AVL(手撕平衡二叉树:谢谢力扣官方)

// 平衡二叉搜索树结点
struct Node {int val;Node * parent;Node * left;Node * right;int size;int height;Node(int val) {this->val = val;this->parent = nullptr;this->left = nullptr;this->right = nullptr;this->height = 0; // 结点高度:以node为根节点的子树的高度(高度定义:叶结点的高度是0)this->size = 1; // 结点元素数:以node为根节点的子树的节点总数}Node(int val, Node * parent) {this->val = val;this->parent = parent;this->left = nullptr;this->right = nullptr;this->height = 0; // 结点高度:以node为根节点的子树的高度(高度定义:叶结点的高度是0)this->size = 1; // 结点元素数:以node为根节点的子树的节点总数}Node(int val, Node * parent, Node * left, Node * right) {this->val = val;this->parent = parent;this->left = left;this->right = right;this->height = 0; // 结点高度:以node为根节点的子树的高度(高度定义:叶结点的高度是0)this->size = 1; // 结点元素数:以node为根节点的子树的节点总数}
};// 平衡二叉搜索树(AVL树):允许重复值
class AVL {
public:AVL(vector<int> & vals) {if (!vals.empty()) {root = build(vals, 0, vals.size() - 1, nullptr);}}// 根据vals[l:r]构造平衡二叉搜索树 -> 返回根结点Node * build(vector<int> & vals, int l, int r, Node * parent) {int m = (l + r) >> 1;Node * node = new Node(vals[m], parent);if (l <= m - 1) {node->left = build(vals, l, m - 1, node);}if (m + 1 <= r) {node->right = build(vals, m + 1, r, node);}recompute(node);return node;}// 返回二叉搜索树中第k小的元素int kthSmallest(int k) {Node * node = root;while (node != nullptr) {int left = getSize(node->left);if (left < k - 1) {node = node->right;k -= left + 1;} else if (left == k - 1) {break;} else {node = node->left;}}return node->val;}void insert(int v) {if (root == nullptr) {root = new Node(v);} else {// 计算新结点的添加位置Node * node = subtreeSearch(root, v);bool isAddLeft = v <= node->val; // 是否将新结点添加到node的左子结点if (node->val == v) { // 如果值为v的结点已存在if (node->left != nullptr) { // 值为v的结点存在左子结点,则添加到其左子树的最右侧node = subtreeLast(node->left);isAddLeft = false;} else { // 值为v的结点不存在左子结点,则添加到其左子结点isAddLeft = true;}}// 添加新结点Node * leaf = new Node(v, node);if (isAddLeft) {node->left = leaf;} else {node->right = leaf;}rebalance(leaf);}}// 删除值为v的结点 -> 返回是否成功删除结点bool Delete(int v) {if (root == nullptr) {return false;}Node * node = subtreeSearch(root, v);if (node->val != v) { // 没有找到需要删除的结点return false;}// 处理当前结点既有左子树也有右子树的情况// 若左子树比右子树高度低,则将当前结点替换为右子树最左侧的结点,并移除右子树最左侧的结点// 若右子树比左子树高度低,则将当前结点替换为左子树最右侧的结点,并移除左子树最右侧的结点if (node->left != nullptr && node->right != nullptr) {Node * replacement = nullptr;if (node->left->height <= node->right->height) {replacement = subtreeFirst(node->right);} else {replacement = subtreeLast(node->left);}node->val = replacement->val;node = replacement;}Node * parent = node->parent;Delete(node);rebalance(parent);return true;}private:Node * root;// 删除结点p并用它的子结点代替它,结点p至多只能有1个子结点void Delete(Node * node) {if (node->left != nullptr && node->right != nullptr) {return;// throw new Exception("Node has two children");}Node * child = node->left != nullptr ? node->left : node->right;if (child != nullptr) {child->parent = node->parent;}if (node == root) {root = child;} else {Node * parent = node->parent;if (node == parent->left) {parent->left = child;} else {parent->right = child;}}node->parent = node;}// 在以node为根结点的子树中搜索值为v的结点,如果没有值为v的结点,则返回值为v的结点应该在的位置的父结点Node * subtreeSearch(Node * node, int v) {if (node->val < v && node->right != nullptr) {return subtreeSearch(node->right, v);} else if (node->val > v && node->left != nullptr) {return subtreeSearch(node->left, v);} else {return node;}}// 重新计算node结点的高度和元素数void recompute(Node * node) {node->height = 1 + max(getHeight(node->left), getHeight(node->right));node->size = 1 + getSize(node->left) + getSize(node->right);}// 从node结点开始(含node结点)逐个向上重新平衡二叉树,并更新结点高度和元素数void rebalance(Node * node) {while (node != nullptr) {int oldHeight = node->height, oldSize = node->size;if (!isBalanced(node)) {node = restructure(tallGrandchild(node));recompute(node->left);recompute(node->right);}recompute(node);if (node->height == oldHeight && node->size == oldSize) {node = nullptr; // 如果结点高度和元素数都没有变化则不需要再继续向上调整} else {node = node->parent;}}}// 判断node结点是否平衡bool isBalanced(Node * node) {return abs(getHeight(node->left) - getHeight(node->right)) <= 1;}// 获取node结点更高的子树Node * tallChild(Node * node) {if (getHeight(node->left) > getHeight(node->right)) {return node->left;} else {return node->right;}}// 获取node结点更高的子树中的更高的子树Node * tallGrandchild(Node * node) {Node * child = tallChild(node);return tallChild(child);}// 重新连接父结点和子结点(子结点允许为空)static void relink(Node * parent, Node * child, bool isLeft) {if (isLeft) {parent->left = child;} else {parent->right = child;}if (child != nullptr) {child->parent = parent;}}// 旋转操作void rotate(Node * node) {Node * parent = node->parent;Node * grandparent = parent->parent;if (grandparent == nullptr) {root = node;node->parent = nullptr;} else {relink(grandparent, node, parent == grandparent->left);}if (node == parent->left) {relink(parent, node->right, true);relink(node, parent, false);} else {relink(parent, node->left, false);relink(node, parent, true);}}// trinode操作Node * restructure(Node * node) {Node * parent = node->parent;Node * grandparent = parent->parent;if ((node == parent->right) == (parent == grandparent->right)) { // 处理需要一次旋转的情况rotate(parent);return parent;} else { // 处理需要两次旋转的情况:第1次旋转后即成为需要一次旋转的情况rotate(node);rotate(node);return node;}}// 返回以node为根结点的子树的第1个元素static Node * subtreeFirst(Node * node) {while (node->left != nullptr) {node = node->left;}return node;}// 返回以node为根结点的子树的最后1个元素static Node * subtreeLast(Node * node) {while (node->right != nullptr) {node = node->right;}return node;}// 获取以node为根结点的子树的高度static int getHeight(Node * node) {return node != nullptr ? node->height : 0;}// 获取以node为根结点的子树的结点数static int getSize(Node * node) {return node != nullptr ? node->size : 0;}
};class Solution {
public:int kthSmallest(TreeNode * root, int k) {// 中序遍历生成数值列表vector<int> inorderList;inorder(root, inorderList);// 构造平衡二叉搜索树AVL avl(inorderList);// 模拟1000次插入和删除操作vector<int> randomNums(1000);std::random_device rd;for (int i = 0; i < 1000; ++i) {randomNums[i] = rd()%(10001);avl.insert(randomNums[i]);}shuffle(randomNums); // 列表乱序for (int i = 0; i < 1000; ++i) {avl.Delete(randomNums[i]);}return avl.kthSmallest(k);}private:void inorder(TreeNode * node, vector<int> & inorderList) {if (node->left != nullptr) {inorder(node->left, inorderList);}inorderList.push_back(node->val);if (node->right != nullptr) {inorder(node->right, inorderList);}}void shuffle(vector<int> & arr) {std::random_device rd;int length = arr.size();for (int i = 0; i < length; i++) {int randIndex = rd()%length;swap(arr[i],arr[randIndex]);}}
};

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

再来介绍另一个binlog文件解析的第三方工具my2sql

看腻了文字就来听听视频演示吧&#xff1a;https://www.bilibili.com/video/BV1rp4y1w74B/ github项目&#xff1a;https://github.com/liuhr/my2sql gitee链接&#xff1a;https://gitee.com/mirrors/my2sql my2sql go版MySQL binlog解析工具&#xff0c;通过解析MySQL bin…

Maven 中引用其他项目jar包出现BOOT-INF问题

问题 在B项目中引入A项目的类&#xff0c;但是发现怎么也引入不进来 A项目打包之后&#xff0c;想在B项目中引用jar 在B项目中发现类文件无法引用 参考网上进行清缓存等一系列操作都没有解决。 最后发现引用的jar包中包含BOOT-INF&#xff0c; 然后去A项目中查找&#xff…

基于回溯搜索优化的BP神经网络(分类应用) - 附代码

基于回溯搜索优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于回溯搜索优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.回溯搜索优化BP神经网络3.1 BP神经网络参数设置3.2 回溯搜索算法应用 4.测试结果…

基于MFC和OpenCV实现人脸识别

基于MFC和OpenCV实现人脸识别 文章目录 基于MFC和OpenCV实现人脸识别1. 项目说明1. 创建项目2. 启动窗口3. 登录窗口-添加窗口、从启动窗口跳转4. 启动窗口-美化按钮5. 登录窗口-美化按钮、雪花视频6. 注册窗口-美化按钮、雪花视频、从启动窗口跳转7. 注册窗口-开启摄像头8. 注…

大恒IFrameData IImageData转bmp HObject Mat

大恒工业相机采集的帧数据转为其他8bit图像格式 C#转为bmp格式转为Halcon的HObject格式转为OpenCVSharp的Mat格式 回调采集图像的数据类型为IFrameData&#xff0c;单帧采集的数据类型为IImageData&#xff0c;两者的区别为IImageData类多了一个**Destroy()**方法 C# 转为bm…

C++标准模板(STL)- 类型支持 (定宽整数类型)(int8_t,int_fast8_t,int_least8_t,intmax_t,intptr_t)

定宽整数类型 类型 定义于头文件 <cstdint> int8_tint16_tint32_tint64_t (可选) 分别为宽度恰为 8、16、32 和 64 位的有符号整数类型 无填充位并对负值使用补码 &#xff08;仅若实现支持该类型才提供&#xff09; (typedef) int_fast8_tint_fast16_tint_fast32_tint…

第二章 线性表

线性表 线性表的基本概念线性表的顺序存储线性表顺序存储的类型定义线性表基本运算在顺序表上的实现顺序表实现算法的分析 线性表的链接存储单链表的类型定义线性表的基本运算在单链表上的实现 其他运算在单链表上的实现建表删除重复结点 其他链表循环链表双向循环链表 顺序实现…

如何将图片存到数据库(以mysql为例), 使用ORM Bee更加简单

如何将图片存到数据库 1. 创建数据库: 2. 生成Javabean public class ImageExam implements Serializable {private static final long serialVersionUID 1596686274309L;private Integer id;private String name; // private Blob image;private InputStream image; //将In…

【算法练习Day12】树的递归遍历非递归遍历

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 递归遍历前序遍历中序遍历后…

《计算机视觉中的多视图几何》笔记(12)

12 Structure Computation 本章讲述如何在已知基本矩阵 F F F和两幅图像中若干对对应点 x ↔ x ′ x \leftrightarrow x x↔x′的情况下计算三维空间点 X X X的位置。 文章目录 12 Structure Computation12.1 Problem statement12.2 Linear triangulation methods12.3 Geomet…

AndroidStudio精品插件集

官网 项目地址&#xff1a;Github博客地址&#xff1a;Studio 精品插件推荐 使用需知 所有插件在 Android Studio 2022.3.1.18&#xff08;长颈鹿&#xff09;上测试均没有问题&#xff0c;推荐使用此版本Android Studio 2022.3.1.18&#xff08;长颈鹿&#xff09;正式版下…

计算机网络(六):应用层

参考引用 计算机网络微课堂-湖科大教书匠计算机网络&#xff08;第7版&#xff09;-谢希仁 1. 应用层概述 应用层是计算机网络体系结构的最顶层&#xff0c;是设计和建立计算机网络的最终目的&#xff0c;也是计算机网络中发展最快的部分 早期基于文本的应用 (电子邮件、远程登…

【计算机网络】HTTPS协议详解

文章目录 一、HTTPS协议 介绍 1、1 HTTP协议不安全的体现 1、2 什么是 HTTPS协议 二、加密的一些概念 2、1 怎么理解加密 2、2 为什么要加密 2、3 常见的加密方式 2、2、1 对称加密 2、2、2 非对称加密 三、HTTPS协议探究加密过程 3、1 只使用对称加密 3、2 只是用非对称加密 3…

JVM篇---第三篇

系列文章目录 文章目录 系列文章目录一、什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?二、Java内存结构三、说说对象分配规则一、什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文…

23.3 Bootstrap 框架4

1. 轮播 1.1 轮播样式 在Bootstrap 5中, 创建轮播(Carousel)的相关类名及其介绍: * 1. carousel: 轮播容器的类名, 用于标识一个轮播组件. * 2. slide: 切换图片的过渡和动画效果. * 3. carousel-inner: 轮播项容器的类名, 用于包含轮播项(轮播图底下椭圆点, 轮播的过程可以显…

【Docker】搭建 Docker 镜像仓库

文章目录 前言&#xff1a;公有仓库和私有仓库公共镜像仓库私有镜像仓库 一、搭建 Docker 镜像仓库1.1 搭建简化版的镜像仓库1.2 搭建带有图形化界面的镜像仓库1.3 配置 Docker 信任地址 二、向私有镜像仓库推送和拉取镜像2.1 推送本地镜像到私有仓库2.2 拉取私有仓库中的镜像 …

机器学习笔记(二)

过拟合 如下图左边,模型出现了过拟合现象 为了解决过拟合现象, 其中一个做法是多收集数据,如右图。 第二种做法是减少模型的特征数量,即x 第三种做法是正则化 正则化就是减少x前面的参数 w的数值, 不用消除x 正则化的梯度下降如下, 因为只是缩小了w的值,而 b的值保持不变 …

通过BeanFactotyPostProcessor动态修改@FeignClient的path

最近项目有个需求&#xff0c;要在启动后&#xff0c;动态修改FeignClient的请求路径&#xff0c;网上找到的基本都是在FeignClient里使用${…}&#xff0c;通过配置文件来定义Feign的接口路径&#xff0c;这并不能满足我们的需求 由于某些特殊原因&#xff0c;我们的每个接口…

floyd算法细节

这个不是一篇学习性文章 主要是针对这几天思考的问题进行一些回答 floyD在计网和数据结构和图模型中有广泛的应用算法 很简单但是其中蕴含的原理值得细究。 弗洛伊德算法(Floyd)主要针对多源最短路径,且可以解决路径中有负权的情况(不包含负权回路),但是迪杰斯特拉算法只…

uni-app:实现页面效果3

效果 代码 <template><view><!-- 风速风向检测器--><view class"content_position"><view class"content"><view class"SN"><view class"SN_title">设备1</view><view class&quo…