C++ 笛卡尔树排序2025年3月21日 | 阅读13分钟 笛卡尔树排序 是一种独特的排序算法,它利用笛卡尔树信息结构来实现对一系列精彩数字的高效排序。要理解这套规则,必须深入研究笛卡尔树的概念、它们的生成以及排序过程。 笛卡尔树是从给定的不重复数字序列派生出的二叉树。树中的每个节点代表输入序列中的一个元素,并具有关键属性:一个值和一个关联的优先级。节点的优先级是根据其在输入序列中的函数确定的。具体来说,在序列中较早出现的节点比后来出现的节点具有更高的优先级。笛卡尔树的生成涉及一个系统化的过程,该过程遍历整个输入序列。 在遇到每个元素时,都会创建一个相应的节点并将其插入树中,同时遵循保持基于优先级的排序的规则。基于栈的方法通常在此过程中使用,以确保在添加新节点时保留笛卡尔树的属性。 插入涉及与现有节点的比较,从而导致树结构的调整以保持正确的笛卡尔树属性。该算法的效率在处理不重复数字序列时尤为突出,并且在生成笛卡尔树符合整体计算目标的情况下,其性能可能很有优势。 方法一:递归笛卡尔树排序递归笛卡尔树排序是笛卡尔树排序的一种实现,它使用递归方法来构建笛卡尔树并执行中序遍历。笛卡尔树是从一系列精彩数字派生出的二叉树,它在对整个输入序列进行排序中起着至关重要的作用。 笛卡尔树输入序列的构建方式是,树中的每个节点的值对应序列中的一个元素,并且节点的优先级通过元素的顺序确定,而笛卡尔树属性确保树的中序遍历会产生排序后的序列。 递归方法递归笛卡尔树排序方法涉及通过对子数组递归地构建笛卡尔树来将问题分解为更小的子问题。 程序输出 5 10 40 30 28 解释 节点结构 “Node”结构代表笛卡尔树中的一个节点。它包含三个组件:“value”,存储节点的值;“left”和“right”,它们是指向左子节点和右子节点的指针。 buildCartesianTree 函数
inOrderTraversal 函数
cartesianTreeSort 函数
主函数 在主函数中,定义了一个示例数组 arr:{5, 10, 40, 30, 28}。计算数组的大小,并使用数组及其大小调用 cartesianTreeSort。 程序打印排序后的序列,这是笛卡尔树排序算法的结果。 复杂度分析 时间复杂度分析 构建笛卡尔树(buildCartesianTree 函数) 该函数通过遍历每个元素一次来递归地构建笛卡尔树。 在递归函数的每一层,它都会处理固定数量的元素,包括查找最小值和创建节点等任务。 构建笛卡尔树所需的时间复杂度为O(n log n),其中“n”表示输入数组中包含的元素数量。 中序遍历(inOrderTraversal 函数) 中序遍历访问每个节点一次。 中序遍历的时间复杂度为 O(n),其中 n 是笛卡尔树中的节点数。 将构建笛卡尔树和执行中序遍历的时间复杂度相结合,笛卡尔树排序的总时间复杂度为O(n log n)。 空间复杂度分析 节点结构 输入数组中的每个元素对应笛卡尔树中的一个节点。存储这些节点所需的空间随数组中元素数量的增长而增长。 节点结构的空间复杂度为O(n)。 递归栈 buildCartesianTree 函数中的递归调用会消耗调用栈空间。 构建笛卡尔树所用递归的最大深度对应于树的高度。在最坏的情况下,笛卡尔树的高度为log(n),其中 n 是输入数组中包含的元素数量。 由于递归栈造成的空间复杂度为O(log n)。 将节点结构和递归栈的空间复杂度相结合,笛卡尔树排序的总空间复杂度为O(n + log n)。 在实践中,主要因素是节点结构所需的空间,而递归栈的贡献相对较小。 方法二:带栈的迭代笛卡尔树排序带栈的迭代笛卡尔树排序涉及使用迭代方法和栈数据结构来构建笛卡尔树。该方法避免了递归方法中常用的递归调用。栈用于在迭代过程中跟踪部分笛卡尔树。主要步骤包括节点构造、基于栈的树构造和用于排序的中序遍历。 程序输出 Sorted Sequence: 5 10 40 30 28 解释 节点构造
基于栈的树构造
最终笛卡尔树 迭代完成后栈顶代表笛卡尔树的根。 用于排序的中序遍历
关键点
复杂度分析 时间复杂度 节点构造和栈操作 输入序列中的每个元素仅处理一次。对于每个元素,算法都会执行固定数量的栈操作。 考虑到节点构造和栈操作,总时间复杂度为O(n),其中“n”表示输入序列中包含的元素数量。 中序遍历 中序遍历对笛卡尔树中的每个节点执行一次。 中序遍历的时间复杂度为O(n),其中 n 是笛卡尔树中的节点数。 将节点构造、栈操作和中序遍历的复杂度相结合,带栈的迭代笛卡尔树排序的总时间复杂度为O(n)。 空间复杂度 节点结构 输入数组中的每个元素都代表笛卡尔树中的一个节点。存储这些节点所需的空间随数组中元素的数量而增长。 节点结构的空间复杂度为O(n)。 栈(s 和 inOrderStack) 该算法使用两个栈(s 用于树构造,inOrderStack 用于遍历)。 每个栈的最大大小与笛卡尔树的高度成正比,在最坏的情况下为 log(n)。 两个栈的空间复杂度均为O(log n)。 将节点结构和两个栈的空间复杂度相结合,带栈的迭代笛卡尔树排序的总空间复杂度为O(n + log n)。 在实践中,主要因素是节点结构所需的空间,而栈的贡献相对较小。 该算法效率很高,具有线性时间复杂度,适合排序大型数据集。空间复杂度也合理,算法在实践中表现良好。 方法三:使用线段树使用线段树的笛卡尔树排序涉及高效查找范围内的最小元素。这种方法通过在输入数组的每个范围内选择最小元素来构建笛卡尔树。关键思想是使用线段树数据结构来高效地回答范围最小查询。 线段树概述线段树是一种二叉树数据结构,用于存储有关区间或段的信息。 线段树的每个叶子节点代表输入数组中的一个元素。 每个非叶子节点存储其子节点的最小值。 使用线段树的笛卡尔树排序节点结构 Node 代表笛卡尔树结构中的一个节点。每个节点包含最小值、该最小值的索引以及指向其左子节点和右子节点的指针或引用。 构建笛卡尔树 笛卡尔树是使用从线段树获得的最小值递归构建的。 在每一步中,通过利用线段树查询操作来确定当前范围内的最小元素和索引。然后使用此最小值和索引生成一个 Node。此过程依次对左子范围和右子范围进行。 线段树操作构建 构建线段树以表示输入数组中不同子范围的最小值。 构建操作通常自底向上进行。 查询 查询操作用于查找给定范围内的最小元素及其索引。 它以对数时间有效地查找指定范围内的最小值。 中序遍历构建笛卡尔树后,执行中序遍历以输出排序后的序列。 程序输出 Sorted Sequence: 5 10 40 30 28 解释 节点结构(Node) Node 结构表示笛卡尔树中的一个节点。 它包含三个重要属性: value:表示范围内的最小值。 index:保存最小值的索引。 left 和 right:指向节点左右子节点的指针。 Build Cartesian Tree(buildCartesianTree) 此函数递归地构建笛卡尔树。关键操作是使用线段树查询在给定范围内查找最小值及其索引。 对于每个范围,都会创建一个带有最小值和索引的节点,并对左子范围和右子范围递归地调用该函数。 这确保了通过在每个范围内选择最小元素来构建笛卡尔树。 Build Segment Tree(buildSegmentTree) 此函数初始化并构建线段树。 它设置必要的数据结构,并调用 buildCartesianTree 函数来使用线段树创建笛卡尔树。 In-Order Traversal(inOrderTraversal)
Cartesian Tree Sort(cartesianTreeSort)
输出 程序的输出是使用线段树通过笛卡尔树排序获得的输入序列的排序结果。
复杂度分析 时间复杂度 Build Cartesian Tree(buildCartesianTree) 构建操作涉及通过在每个范围内选择最小元素来构建笛卡尔树。 对于笛卡尔树中的每个节点,都会执行线段树查询以查找子范围内的最小值。 由于笛卡尔树中有 O(N) 个节点,因此构建笛卡尔树的总时间复杂度为O(N log N)。 Build Segment Tree(buildSegmentTree) 线段树的构建操作涉及初始化和构造树。 因此,构建线段树的总时间复杂度为O(N)。 In-Order Traversal(inOrderTraversal) 笛卡尔树的中序遍历是一个线性时间操作,因为每个节点都只访问一次。 中序遍历的时间复杂度为O(N),其中 N 是笛卡尔树中的节点数。 Cartesian Tree Sort(cartesianTreeSort) 总时间复杂度主要由构建笛卡尔树决定,为 O(N log N)。 空间复杂度 节点结构(Node) 笛卡尔树中的每个节点都需要恒定的空间,因为它只存储几个变量(值、索引、左右子节点)。 每个节点所需的空间复杂度为O(1)。 Build Cartesian Tree(buildCartesianTree) 空间复杂度由笛卡尔树中的节点数决定。 由于笛卡尔树中有O(N)个节点,因此空间复杂度为O(N)。 Build Segment Tree(buildSegmentTree) 线段树的空间复杂度为O(4N),因为线段树通常使用数组实现,对于 N 个元素,需要 4N 个节点。 每个节点存储有关输入数组中范围的信息。 In-Order Traversal(inOrderTraversal) 中序遍历所需的空间很小,通常为O(1),因为它不使用随输入大小增长的附加数据结构。 Cartesian Tree Sort(cartesianTreeSort) 总空间复杂度主要由笛卡尔树决定,为O(N)。 结论笛卡尔树排序是一种利用笛卡尔树数据结构的独特方法。该算法的优雅之处在于其将树构造与排序过程无缝集成。这种集成为排序不重复数字序列提供了一种高效且直观的方法。理解笛卡尔树背后的原理、它们的构造以及随后的中序遍历,可以深入了解笛卡尔树排序的理论基础和实际应用。 下一个主题C++ 与 Kotlin 的区别 |
二叉树遍历是计算机科学中的一项基本操作,对于搜索、排序和求值表达式等众多应用至关重要。在各种二叉树遍历类型中,前序遍历因其“先根”方法而占有重要地位。在前序遍历中,序列...
阅读 15 分钟
在本文中,我们将通过几个示例讨论如何在 C++ 中将句子编码为 Pig Latin。Pig Latin 加密是一种将普通句子编码为异常句子的技术。将特定句子转换为 Pig Latin 的规则是:首先,将句子分解为...
阅读 4 分钟
确定时钟上时针和分针之间的角度是常见的编程问题,它结合了逻辑和数学。虽然时针每分钟旋转 0.5°,而分针每分钟旋转 6°。C++ 中的目标是预测...
5 分钟阅读
在本文中,我们将讨论 C++ 中的 std::logic_error 方法及其语法、示例和优点。C++ 中的 std::logic_error 方法是什么?C++ 中声明在标头文件中的异常类称为 std::logic_error。它用于报告程序中的逻辑错误,包括...
阅读 3 分钟
C++17 中的 <charconv> 标头文件 <charconv> 标头包含几种将字符序列转换为数值信息以及反之亦然的方法。与相同目的的 <cstdlib> 标头文件函数相比,它被认为更有效。<charconv> 标头文件提供的函数是...
阅读 3 分钟
强大的编程语言 C++ 一直在塑造当代软件开发格局方面发挥着重要作用。C++ 编译器是一个至关重要但经常被忽视的元素,它为每个成功的 C++ 程序提供动力。本文探讨了 C++ 编译器在...
阅读 6 分钟
C++ 淘汰赛游戏涉及按顺序移除 1 到 n 的每个数字,直到只剩下一个。每一轮都从左到右开始移除并改变方向。每一轮,移除一半剩余的棋子。这个问题的实际解决方案...
阅读 4 分钟
在本文中,我们将讨论如何在 C++ 中查找前 N 个 Iccanobif 数。在实现之前,我们必须了解 C++ 中的 Iccanobif 数。什么是 C++ 中的 Iccanobif 数?Iccanobif 数与斐波那契数相似。与斐波那契数一样,iccanobif 数……
5 分钟阅读
在本文中,我们将讨论带实现。简介:纸牌翻转游戏是一种简单但有趣的游戏,玩家将牌面朝下放在网格中进行翻转。此游戏的目标是通过一次翻转两张牌来找到匹配的对...
阅读 6 分钟
在本文中,我们将讨论 C++ 的居中九角数程序。但在其实现之前,我们必须了解 C++ 中的居中九角数。什么是居中九角数?表示有 K 个点的中心九边形的数字称为...
阅读 4 分钟
我们请求您订阅我们的新闻通讯以获取最新更新。
我们提供所有技术(如 Java 教程、Android、Java 框架)的教程和面试问题
G-13, 2nd Floor, Sec-3, Noida, UP, 201301, India