【发布时间】:2016-12-31 17:18:10
【问题描述】:
到目前为止,我一直在使用左右指针实现二叉搜索树,例如:
template<typename T>
struct BSTNode{
BSTNode* left;
BSTNode* right;
T data;
}
我遇到了节点也有指向父节点的指针的实现。你为什么想这么做?有哪些取舍?
【问题讨论】:
到目前为止,我一直在使用左右指针实现二叉搜索树,例如:
template<typename T>
struct BSTNode{
BSTNode* left;
BSTNode* right;
T data;
}
我遇到了节点也有指向父节点的指针的实现。你为什么想这么做?有哪些取舍?
【问题讨论】:
从某个角度来看,您的问题是有效的,因为 parent 指针在结构中引入了冗余,这在几种情况下是可以避免的。但是在二叉树的情况下,这会给您带来巨大的好处,您可以“向上”跳一级(即从一个节点到其父节点),而无需记住父节点的地址。如果节点的父节点已知,则可以非常有效且简单地实现几种算法(例如,获取两个值之间的节点数)。
权衡是冗余:如果您修改树的结构(例如通过平衡树),您必须记住同时更新left/right 和parent 指针保持树的一致性。
【讨论】:
二叉搜索树指的是相当普遍的二叉树。 对于 二叉搜索树,没有理由有父指针。
但是,二叉树还有更特殊的变体,其中父指针是有益的。例如,寻找 AVL 树 或 红黑树。这些特化对树的布局施加了进一步的限制以实现各种目标,例如通过确保树始终保持平衡红黑树中的查找/插入/删除保证O(log n) 复杂性/em>。
为了满足这些限制,父指针有时会派上用场。当然,它是通过用内存(指针)换取速度(通过算法查找父级)来实现的。
查看您最喜欢的有关数据结构的书籍以了解其原理和原因,或者查阅维基百科。
【讨论】:
它为您提供了不会因树突变而失效的更简单和更小的迭代器。如果没有父节点,遍历例程需要一个大小等于树深度的堆栈来记住它所在的位置。如果添加或删除节点,此堆栈将变得陈旧,并且迭代器不再正常工作,例如它可能会记得重新访问已删除的祖先节点。
当然,保留父指针的代价是内存中的树更大。我不能对性能做出任何声明,因为更新父指针需要时间,但也消除了在某些算法中记住父指针的步骤。因此,权衡归结为一方面拥有一棵具有大而脆弱的迭代器的小树,另一方面拥有一棵具有小型健壮迭代器的大树。
【讨论】:
这是一种以速度换空间的交易,在概念上不必进行,即它是一个实现细节。您始终可以编写一个函数,该函数返回 BST 中任何给定节点的父节点,但调用这样的函数需要时间,通常只查找一个值会更快。
如果您不愿意进行交易,请考虑使用我实现的XOR BST,这是一种妥协。
【讨论】: