[英]Binary search tree character implementation C++
我正在尝试实现字符 BST。 我无法理解插入字符的逻辑。 所以假设这是在 main insert("a");
insert("b");
insert("c");
insert("d");
a字母什么时候会小于a? 那么我的树基本上都在右边吗?
a
\
b
\
c
\
d
像那样? 因为永远不会有小于 a 的字母。 但这感觉不对,但我不确定我错过了什么。
我插入 function:
void insert(char letter, node * curr)
{
int count{0};
if ( strcmp(curr->getLetter(), letter) == 0)
return 0;
if (!curr->getRight() && strcmp(curr->getLetter(), letter) > 0)
{
curr->getRight() = new node(letter);
return 1;
}
if (!curr->getLeft() && strcmp(curr->getLetter(), letter) < 0)
{
curr->getLeft() = new node(letter);
return 1;
}
if (strcmp(letter, curr->getLetter() ) < 0)
count += insert(letter, curr->getLeft() );
else
count += insert(letter, curr->getRight() );
return count;
}
看起来奇怪的生成“树”可能感觉不对,但事实并非如此。 您看到的是一棵退化的二叉树,特别是已经退化为链表的二叉树。 这是在没有重新平衡的情况下插入有序二叉树的惩罚。 这也是平衡算法和自平衡树存在的主要原因。 没有它们,退化的条件就会浮出水面,这首先会消除拥有二叉树的所有辉煌:快速 O(logN) 搜索、插入和删除渐近复杂性。
例如,您的原始广告订单生成了以下内容:
a
\
b
\
c
\
d
显然这被下放到一个链表。
但是现在,让我们再试一次,只是这一次,在每次插入之后,您检查以确保没有节点的子节点的失衡程度超过 +1(或 -1)级别。 这是保持AVL 树平衡的一部分(但绝不是所有部分;它比您将要看到的要复杂得多)。 我选择 AVL 来演示它只是因为它很容易理解。 接着就,随即...
插入a
a
第一个节点a
显然是微不足道的。 它没有孩子,所以 L/R 平衡是 0/0,我们继续。
使用b
插入,我们将有:
a
\
b
b
为0/0, a
节点为0/1,没问题。 现在插入c
a
\
b
\
c
此时b
的 L/R 余额为 0/1,但a
的余额为 0/2。 正是在这一点上会发生再平衡。 对于这种微不足道的条件,将发生关于a
的左旋。 它是由
a
的右孩子b
。a
的右孩子设置为它以前的右孩子b
的左孩子(没有,所以......很简单。b
的左孩子设置为a
a
人设置为现在指向b
。 在这种情况下,这将是树的“根”指针。 即现在b
是根。结果如下所示:
b
/ \
a c
了不起。 这太棒了。 a
为0/0, b
为1/ c
为0/0
插入d
,
b
/ \
a c
\
d
这棵树仍然是“平衡的”,因为没有节点的子深度比其他子节点的深度大 +1。 具体来说,
a
0/0b
是 1/2c
是 0/1d
是 0/0哎呀。
继续这个例子,让我们看看当我们插入e
时会发生什么。 在重新平衡之前,它看起来像这样:
b
/ \
a c
\
d
\
e
现在,从我们刚刚挂起那个e
节点的地方开始工作:
e
是 0/0(显然)d
为 0/1c
是 0/2 <=== 检测到不平衡停在那里片刻,我们看到我们应该像之前对a
所做的那样对c
进行左旋转:
b
/ \
a d
/ \
c e
现在我们的节点余额是:
a
0/0b
是 1/2c
是 0/0d
为 1/1e
是 0/0在另一个子节点的任一方向上,没有节点的子节点平衡偏离超过 1。 树再次平衡。
作为最后一个例子,我们使用之前的树,这次再挂一个节点: f
。
b
/ \
a d
/ \
c e
\
f
从f
开始往上看,我们看到:
f
是 0/0e
是 0/1d
为 1/2b
是 1/3 <=== 检测到不平衡哇。 在我们再次这样做之前,我们已经按照旋转机制做了几次:
b
的右孩子d
。b
的右孩子设置为它以前的右孩子d
的左孩子c
。d
的左孩子设置为b
b
的人设置为现在指向d
。 在这种情况下,这将是树的“根”指针。 即现在d
是根。结果如下所示:
d
/ \
b e
/ \ \
a c f
最终的平衡因素是:
a
0/0b
是 1/1c
是 0/0d
是 2/2e
是 0/1f
是 0/0 有很多关于不同树木平衡机制的书籍。 我在上面展示的示例在某种程度上使问题变得微不足道,但随着您继续学习树木以及保持树木高效的可用礼仪,这将很重要。 有序标准容器( std::set
、 std::map
等)使用的一种常见自平衡技术是红黑树。 管理与我上面显示的有很大不同,但前提保持不变。 保持任何单个树的深度(无论是整棵树,还是整棵树中的任何子树)都尽可能接近 logN 阶,其中N
是该树中包含的节点数。 保持这种状态是树木可以提供的效率。
了解相同的数据可以多种方式存储在有效的二叉搜索树中。
像这样天真地构造二叉搜索树时,插入数据的顺序很重要。 您发现的是您的插入算法有序数据会给您最坏的情况行为。 尝试随机排列相同的字母,看看会得到什么。
这就是为什么二叉搜索树的实际实现在插入时使用更复杂的算法。 std::map<K,V>
的典型实现例如使用“红黑树”,它们仍然只是二叉搜索树,但插入和删除的方式是保证树不会最终变得过于不平衡无论插入顺序如何。 这些类型的数据结构称为“自平衡二叉搜索树”。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.