我知道如何在O(n)时间内将数组转换为笛卡尔树
但是,所需的内存量太高(常量),因为我需要将左右指针至少与笛卡尔树中的每个节点相关联。
任何人都可以将我的工作联系起来以减少这些常数(希望是1)吗?
答案 0 :(得分:2)
您不需要保持与笛卡尔树节点关联的左右指针。 您只需要保留每个节点的父节点和笛卡尔树的定义 (数组A [0,N-1]的笛卡尔树是二叉树C(A),其根是A的最小元素,用该最小值的位置i标记。根的左子节点是笛卡儿树的A [0,i - 1]如果i> 0,否则就没有孩子。右子的定义类似于A [i + 1,N - 1]。),你可以遍历这个数组,如果节点的父节点具有比节点本身更低的索引,而节点将是其父节点的右子节点,并且类似地,如果节点的父节点具有更高的索引,则节点将保留其父节点的子节点。
希望这有帮助。
答案 1 :(得分:0)
你可以使用堆来存储你的树,基本上它是一个数组,其中数组中的第一个元素是根,第二个是根的左子,右边的第三个等等。它便宜得多但在编程时需要更多的关注。
答案 2 :(得分:0)
有可能只为儿童到父母的引用(按索引)构建一个仅具有额外空间的笛卡尔树:因此,除了输入数组之外,您还需要一个大小相等的数组,并保留与第一个索引相关的索引值数组。如果我们将额外的数组称为parentOf
,则array[parentOf[i]]
将是array[i]
的父级,除非以array[i]
为根。在这种情况下,parentOf[i]
应该类似于NIL指针(例如-1)。
笛卡尔树上的Wikipedia article提供了一种简单的构造方法:
一种方法是简单地以一种允许从上到下遍历树的结构按从左到右的顺序处理序列值。
这可能会给人一种印象,即该算法在树中保持向上和向下链接都是必需的,但事实并非如此。只需维护从孩子到父母的链接即可。
在构造过程中,将新值注入到最右边节点(具有最近添加的值)的路径中。该路径中的任何子项都必然是其父项的 right 子项。
从叶子沿着相反的方向走时,请跟踪父母及其右边的孩子(您来自哪里)。找到插入点后,该子节点将把新节点作为父节点,而新子节点将把“旧”父节点作为其父节点。
在此过程中,您不需要存储指向儿童的指针。
这是用JavaScript编写的算法。例如,从输入数组[9,3,7,1,8,12,10,20,15,18,5]中填充树。仅出于验证目的,将同时打印输入数组和父引用:
class CartesianTree {
constructor() {
this.values = [];
this.parentOf = [];
}
extend(values) {
for (let value of values) this.push(value);
}
push(value) {
let added = this.values.length; // index of the new value
let parent = added - 1; // index of the most recently added value
let child = -1; // a NIL pointer
this.values.push(value);
while (parent >= 0 && this.values[parent] > value) {
child = parent;
parent = this.parentOf[parent]; // move up
}
// inject the new node between child and parent
this.parentOf[added] = parent;
if (child >= 0) this.parentOf[child] = added;
}
}
let tree = new CartesianTree;
tree.extend([9,3,7,1,8,12,10,20,15,18,5]);
printArray("indexes:", tree.values.keys());
printArray(" values:", tree.values);
printArray("parents:", tree.parentOf);
function printArray(label, arr) {
console.log(label, Array.from(arr, value => (""+value).padStart(3)).join(" "));
}