如何生成最大不平衡的AVL树?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (3)
  • 关注 (0)
  • 查看 (110)

我写了一个作为通用排序容器的AVL树的C语言库为了测试,我想要有一种方法来填充一棵树,使它最大程度地不平衡,也就是说,它包含的节点数有最大的高度。

AVL树具有这样的优点:如果从空树开始,按升序(或降序)插入节点,则树总是完全平衡的(也就是说,对于给定的节点数,树有其最小高度)。从空树T0开始,为每个节点n生成精确平衡的AVL树TN的整数键序列是简单的。

  • K1=0
  • Kn+1=kn+1,即kn=n-1

我正在寻找一个(希望是简单的)整数键序列,当插入到最初的空树T0中时,生成的AVL树T0,...,TN都是最大的联合国平衡。

我也会对一个解感兴趣,其中只有最后一棵树TN是最大不平衡的(节点数n将是算法的一个参数)。

满足约束条件的解

  • 最大(K1,...,kn)-min(K1,...,kn)+1≤2n
提问于
用户回答回答于

基本解

Fibonacci树有几个属性,可以用来形成紧凑的Fibonacci树:

  1. Fibonacci树中的每个节点本身就是Fibonacci树。
  2. 高度为n的Fibonacci树的节点数等于fn+2-1.
  3. 节点与其左子节点之间的节点数等于节点的左子节点的右子节点数。
  4. 节点与其右子节点之间的节点数等于该节点的右子节点的左子节点数。

在不失去通用性的情况下,我们将假设Fibonacci树具有以下附加属性:

  1. 如果节点的高度为n,则左子具有n-2,右子具有高度n-1。结合这些性质,我们发现在n高的节点与其左、右子节点之间的节点数等于FN-1-1,我们可以利用这个事实生成紧致Fibonacci树:static int fibs[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170}; void fibonacci_subtree(int root, int height, int *fib) { if (height == 1) { insert_into_tree(root); } else if (height == 2) { insert_into_tree(root + *fib); } else if (height >= 3) { fibonacci_subtree(root - *fib, height - 2, fib - 2); fibonacci_subtree(root + *fib, height - 1, fib - 1); } } ... for (height = 1; height <= max_height; height++) { fibonacci_subtree(0, height, fibs + max_height - 1); }该算法生成给定高度下可能的最小节点数,并产生最小可能范围。可以通过使根节点不是零来改变范围。紧填充算法基本解决方案只产生Fibonacci树,它总是有fn+2-1节点.。如果想要生成一个具有不同节点数的不平衡树,同时仍然最小化范围,那该怎么办?在这种情况下,需要生成下一个更大的Fibonacci树,只需做一些修改:
  2. 序列末尾的一些元素将不会被插入。
  3. 这些因素将造成差距,需要跟踪这些差距的位置。
  4. 节点之间的差异需要适当地缩小。

这里有一种方法仍然利用了解决方案的递归特性:

void fibonacci_subtree(int root, int height, int *fib, int num_gaps, bool prune_gaps)
{
    if(height < 1)
        return;
    if(prune_gaps && height <= 2) {
        if(!num_gaps) {
            if(height == 1) {
                insert_into_tree(root);
            } else if(height == 2) {
                insert_into_tree(root + *fib);
            }
        }
        return;
    }
    if(height == 1) {
        insert_into_tree(root);
    } else {
        int max_rr_gaps = *(fib - 1);
        int rr_gaps = num_gaps > max_rr_gaps ? max_rr_gaps : num_gaps;
        num_gaps -= rr_gaps;

        int max_rl_gaps = *(fib - 2);
        int rl_gaps = num_gaps > max_rl_gaps ? max_rl_gaps : num_gaps;
        num_gaps -= rl_gaps;

        int lr_gaps = num_gaps > max_rl_gaps ? max_rl_gaps : num_gaps;
        num_gaps -= lr_gaps;

        int ll_gaps = num_gaps;
        fibonacci_subtree(root - *fib + lr_gaps, height - 2, fib - 2, lr_gaps + ll_gaps, prune_gaps);
        fibonacci_subtree(root + *fib - rl_gaps, height - 1, fib - 1, rr_gaps + rl_gaps, prune_gaps);
    }
}

主循环稍微复杂一些,以适应任意范围的键:

void compact_fill(int min_key, int max_key)
{
    int num_nodes = max_key - min_key + 1;
    int *fib = fibs;
    int max_height = 0;

    while(num_nodes > *(fib + 2) - 1) {
        max_height++;
        fib++;
    }

    int num_gaps = *(fib + 2) - 1 - num_nodes;

    int natural_max = *(fib + 1) - 1;
    int max_r_gaps = *(fib - 1);
    int r_gaps = num_gaps > max_r_gaps ? max_r_gaps : num_gaps;
    natural_max -= r_gaps;

    int root_offset = max_key - natural_max;

    for (int height = 1; height <= max_height; height++) {
        fibonacci_subtree(root_offset, height, fibs + max_height - 1, num_gaps, height == max_height);
    }
}

闭形解

如果查看基本解决方案生成的每一对单词之间的差异,会发现它们在Fibonacci序列的两个顺序元素之间交替。此交替模式Fibonacci word:

Fibonacci字是二进制数字(或来自任何两个字母的字母的符号)的特定序列.。斐波那契词是由重复级联形成的,就像斐波那契数是由重复加法形成的一样。

结果发现Fibonacci字的闭形解:

static double phi = (1.0 + sqrt(5.0)) / 2.0;

bool fibWord(int n)
{
    return 2 + floor(n * phi) - floor((n + 1) * phi);
}

可以使用这个封闭的解决方案来使用两个嵌套循环来解决这个问题:

// Used by the outer loop to calculate the first key of the inner loop
int outerNodeKey = 0;
int *outerFib = fibs + max_height - 1;

for(int height = 1; height <= max_height; height++) {

    int innerNodeKey = outerNodeKey;
    int *smallFib = fibs + max_height - height + 3; // Hat tip: @WalterTross

    for(int n = fibs[height] - 1; n >= 0; n--) {
        insert_into_tree(innerNodeKey);

        // Use closed-form expression to pick between two elements of the Fibonacci sequence
        bool smallSkip = 2 + floor(n * phi) - floor((n + 1) * phi);
        innerNodeKey += smallSkip ? *smallFib : *(smallFib + 1);
    }

    if(height & 0x1) {
        // When height is odd, add *outerFib.
        outerNodeKey += *outerFib;
    } else {
        // Otherwise, backtrack and reduce the gap for next time.
        outerNodeKey -= (*outerFib) << 1;
        outerFib -= 2;
    }
}

热门问答

腾讯云广州一区DNS变更,需要怎么操作?

思潮澎湃轻描淡写的生活,但思潮澎湃
推荐
我也收到相关的通知了,这里分享下~ 2019年1月31日,腾讯云将对广州地区旧的基础网络DNS服务器(10.225.30.181、10.225.30.223)进行下线。在此期间,腾讯云提供最新的DNS服务器供您更新使用。 我们建议您尽快将DNS服务器配置进行更新,并且我们为您提供...... 展开详请

快照容量与费用的比例?如何关闭停用?

帅的惊动我国计算机大神
推荐已采纳
快照已于2019年1月22日0时启动正式商业化进程,商业化后所有存量快照和新产生的快照将根据快照使用的存储容量进行收费。 在快照商业化后,腾讯云仍旧会在国内主要地域为用户提供一定量的免费额度。免费额度策略如下: 免费额度覆盖范围为中国大陆地域,中国香港及海外地域暂无免费快照额...... 展开详请

无服务器云函数的cron表达式问题?

腾讯云serverless团队

腾讯云 · 产品团队 (已认证)

腾讯云无服务器云函数SCF产品
推荐
https://cloud.tencent.com/document/product/583/9708#cron-.E8.A1.A8.E8.BE.BE.E5.BC.8F.E8.AF.AD.E6.B3.95.E4.B8.80.EF.BC.88.E6.8E.A8.E8.8D.90.E...... 展开详请

云服务器-intelS2 标准入门型 带独立ip么?

Eli Qiao

腾讯 · 高级工程师 (已认证)

腾讯云CVM后台高级研发工程师
推荐

购买时,可以配置wan网ip,也可以之后添加eip

购买云服务器后上面的是否配套有数据库(mysql, sql server)和Web服务器等?

西风

renzha.net · 站长 (已认证)

www.renzha.net
推荐已采纳

买了服务器自己安装配置数据库即可,也可以另外选购性能更高,更安全可靠的云数据库。

无服务器云函数添加触发方式以错误码9000失败?

腾讯云serverless团队

腾讯云 · 产品团队 (已认证)

腾讯云无服务器云函数SCF产品
推荐

实在抱歉,最近这两天由于广州区 api 网关集群的配置量已超上限,导致 api 网关无法新增服务。目前 api 网关的研发同学已经在紧急扩容广州区集群了。

所属标签

扫码关注云+社区