C++的数据结构(九): 笛卡尔树

        笛卡尔树(Cartesian Tree)是一种特殊的二叉树,其每个节点的键值(key)满足二叉搜索树的性质,即左子树上所有节点的键值小于根节点的键值,右子树上所有节点的键值大于根节点的键值。与此同时,每个节点的值(value)又满足堆的性质,即父节点的值总是大于(或小于)其子节点的值。这种双重性质使得笛卡尔树在数据结构和算法领域中具有广泛的应用。

        

        笛卡尔树的构建过程相对直观。给定一个键值(key)和值(value)组成的序列,我们可以通过一次线性扫描来构建对应的笛卡尔树。具体步骤如下: 

        1. 初始化一个空栈,用于保存当前已构建好的树的一部分。
        2. 遍历键值-值序列,对于每个元素,执行以下操作:
                   - 若栈为空,则当前元素作为根节点入栈。
                   - 否则,比较栈顶元素的值与当前元素的值:
                     - 若当前元素的值小于等于栈顶元素的值,则弹出栈顶元素,并重复此步骤,直到找到一个栈顶元素的值小于当前元素的值或栈为空。
                     - 将当前元素作为新的节点,其左子树为最后弹出的那个元素(如果存在的话),右子树为原栈顶元素的右子树(如果存在的话),然后将新节点入栈。

        通过上述步骤,我们可以保证构建出的笛卡尔树满足键值满足二叉搜索树的性质,值满足堆的性质。

        假设我们有一个键值-值序列:[(3, 2), (1, 4), (4, 1), (1, 5), (9, 2), (2, 6), (5, 3), (3, 5)]。根据笛卡尔树的构建原理,我们可以得到如下的笛卡尔树:

     5(3)
     /     \
    1(5)    9(2)
   /  \       \
  3(2) 4(1)    2(6)
 /               \
1(4)            3(5)

        在这个例子中,每个括号里的数字表示节点的值(value),括号外的数字表示节点的键值(key)。可以看出,键值满足二叉搜索树的性质,值满足最大堆的性质。

        笛卡尔树在多个领域有着广泛的应用,包括但不限于:

        1. 范围查询:由于笛卡尔树的键值满足二叉搜索树的性质,我们可以利用这一特性进行高效的范围查询。例如,查找某个键值范围内的所有元素。

        2. 单调栈/单调队列的替代:在某些场景中,我们可以使用笛卡尔树来替代单调栈或单调队列,以支持更加复杂的操作或查询。

        3. 树形数据结构的构建:笛卡尔树可以用于构建具有特殊性质的树形数据结构,如最大堆性质的二叉搜索树,这在某些算法问题中非常有用。

        4. 可视化与表示:由于笛卡尔树同时结合了搜索树和堆的特点,因此在需要同时展示数据间的层次关系和大小关系时,可以使用笛卡尔树进行可视化表示。

        下面是一个使用C++实现的笛卡尔树构建和中序遍历的完整示例,代码如下。

#include <iostream>
#include <vector>
#include <stack>struct Node {int key;int value;Node* left;Node* right;Node(int k, int v) : key(k), value(v), left(nullptr), right(nullptr) {}
};Node* buildCartesianTree(const std::vector<std::pair<int, int>>& arr) {std::stack<Node*> st;Node* root = nullptr;for (const auto& kv : arr) {Node* newNode = new Node(kv.first, kv.second);Node* prevNode = nullptr;while (!st.empty() && st.top()->value < newNode->value) {prevNode = st.top();st.pop();}if (prevNode) {newNode->left = prevNode->right;prevNode->right = newNode;} else {root = newNode;}st.push(newNode);}return root;
}void inorderTraversal(Node* node) {if (node == nullptr) return;inorderTraversal(node->left);std::cout << "Key: " << node->key << ", Value: " << node->value << std::endl;inorderTraversal(node->right);
}int main() {std::vector<std::pair<int, int>> arr = {{3, 2}, {1, 4}, {4, 1}, {1, 5}, {9, 2}, {2, 6}, {5, 3}, {3, 5}};Node* root = buildCartesianTree(arr);std::cout << "Inorder Traversal of the Cartesian Tree:" << std::endl;inorderTraversal(root);// 注意:实际使用时需要释放内存,避免内存泄漏// 这里省略了内存释放的代码,以简化示例return 0;
}

        

        上面的代码,定义了一个`Node`结构体来表示笛卡尔树的节点,包含了键值、值以及左右子树的指针。`buildCartesianTree`函数接受一个键值-值对序列,并构建对应的笛卡尔树。`inorderTraversal`函数则用于中序遍历笛卡尔树,并打印每个节点的键值和值。在`main`函数中,我们创建一个键值-值对序列,然后调用`buildCartesianTree`构建笛卡尔树,并使用`inorderTraversal`遍历和打印树的节点。

        需要注意的是,实际使用时需要确保在不再需要笛卡尔树时释放其占用的内存,以避免内存泄漏。在上面的示例中,为了简化代码,省略了内存释放的部分。在实际应用中,应该添加适当的代码来释放每个节点所占用的内存。

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

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

相关文章

C 深入指针(4)

目录 一、字符指针变量 1 初始化 2 与字符串数组的区别 二、数组指针变量 1 初始化 2 二维数组传参本质 三、函数指针变量 1 初始化 2 用法 四、typedef关键字 五、函数指针数组 一、字符指针变量 1 初始化 //VS2022 x64 #include <stdio.h> int main() {…

InnoDB 事务处理机制

文章目录 前言1. 事务处理挑战1.1 事务机制处理的问题1.2 并发事务带来的问题 2. InnodDB 和 ACID 模型2.1 Innodb Buffer Pool2.2 Redo log2.3 Undo log2.4 应用案例 3. 隔离级别和锁机制3.1 事务隔离级别3.1.1 READ UNCOMMITTED3.1.2 READ COMMITTED3.1.3 REPEATABLE READ3.1…

Linux下redis源码编译安装

华子目录 Redis介绍什么是RedisRedis能干什么Redis的特点Redis与memcached对比 redis源码编译安装下载源码包准备安装环境开始编译开始安装 前台启动后台启动redis开启systemctl启动redis测试redis相关知识 Redis介绍 什么是Redis 2008年&#xff0c;意大利的一家创业公司Mer…

xlrd.biffh.XLRDError: Excel xlsx file; not supported报错原因

xlrd库读取xlsx文件时报错 xlrd.biffh.XLRDError: Excel xlsx file; not supported报错原因&#xff1a; xlrd版本为2.1版本&#xff0c;需要读取xlsx文件需要安装xlrd低一些版本1.2.0版本&#xff0c;重新安装重试即可 更换xlrd版本 重新运行

数仓架构之为什么要进行数仓分层

数仓分层这个概念想必大家都很熟悉&#xff0c;不管是在实际的开发工作当中会用的&#xff0c;还是在面试官面试你的时候会问到&#xff1a;你之前的项目是按照什么分层的&#xff0c;分哪几层&#xff0c;数仓分层有什么好处&#xff0c;举个栗子说说。 简而言之&#xff0c;…

element-ui dialog form 弹框表单组件封装

在使用 element-ui 进行后端管理系统开发时&#xff0c;在封装弹框表单时&#xff0c;遇到两个问题&#xff0c;这里进行简单记录&#xff1a; 1、问题一&#xff1a;点击关闭按钮及遮罩层关闭弹框时&#xff0c;页面报错&#xff0c;如下&#xff1a; 子组件封装&#xff1a;…

上海初中生古诗文大会倒计时4个月:单选题真题示例和独家解析

现在距离2024年初中生古诗文大会还有4个多月时间&#xff0c;备考要趁早&#xff0c;因为知识点还是相对比较多的。这些知识点对于初中语文的学习也是很有帮助的。 今天我们继续来看10道选择题真题和详细解析&#xff0c;以下题目截取自我独家制作的在线真题集&#xff0c;都是…

普通人也能创业!轻资产短视频带货项目,引领普通人实现创业梦想

在这个信息爆炸的时代&#xff0c;创业似乎成为了越来越多人的梦想。然而&#xff0c;传统的创业模式 keJ0277 往往伴随着高昂的资金投入和复杂的管理流程&#xff0c;让许多普通人望而却步。然而&#xff0c;现在有一种轻资产短视频带货项目正在悄然兴起&#xff0c;它以其低…

【CSND博客纪念】“创作纪念日:从灵感迸发到小有成就——我的CSND博客创作之旅”

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

Verilog 实现 i2c 协议

在时钟&#xff08;SCL&#xff09;为高电平的时候&#xff0c;数据总线&#xff08;SDA&#xff09;必须保持稳定&#xff0c;所以数据总线&#xff08;SDA&#xff09;在时钟&#xff08;SCL&#xff09;为低电平的时候才能改变。 在时钟&#xff08;SCL&#xff09;为高电平…

【c语言】TIMI哥听课笔记

计算机的组成 主储存器&#xff1a;内存条&#xff0c;硬盘 CPU内部&#xff1a;运算器&#xff0c;控制器&#xff0c;寄存器 进制转化&#xff1a;二转八拆三&#xff0c;二转十六拆四 基本数据类型 常量&#xff1a;整型常量&#xff08;十进制&#xff0c;0x十六&#x…

【Altium】AD-检查原理图中元器件未连接的Passive Pin

1、 文档目标 如何让原理图编译时找出元器件上未连接的Passive Pin 2、 问题场景 当引脚属性&#xff08;Pin type&#xff09;为passive时&#xff0c;原理图编译的默认规则是不会去检查它们是否有连接的。在实际设计过程中&#xff0c;经常会有导线虚连&#xff0c;漏连的事…