2

これはcの単純な二分木ですが、バランスが取れていないようです。どうすればバランスが取れますか?

コード:

/**
 * binary_tree impl
 */

#include <stdio.h>
#include <stdlib.h>


typedef struct _tnode _tnode;
typedef struct _bin_tree _bin_tree;
struct _tnode {
    int data;
    _tnode *parent;
    _tnode *left;
    _tnode *right;
};

_tnode *new_node(int data) {
    _tnode *node = (_tnode*)malloc(sizeof(_tnode));
    node->data = data;
    node->left = NULL;
    node->right = NULL;
    return node;
}

_tnode *add(_tnode *top, int new_data, int (*cmpf)(int, int)) {
    if(top == NULL) {
        top = new_node(new_data);
    } else if(cmpf(top->data, new_data)<=0) {
        if(top->left == NULL) 
            top->left = new_node(new_data);
        else
            add(top->left, new_data, cmpf);
    } else {
        if(top->right == NULL) 
            top->right = new_node(new_data);
        else
            add(top->right, new_data, cmpf);
    }
    return top;
}

int cmp_int(int n1, int n2) {
    return n1 - n2;
}

void print_tree(_tnode *top) {
    if(top->left) print_tree(top->left);
    printf("%d\n",top->data);
    if(top->right) print_tree(top->right);
}

int main(int argc, char * argv[]) {
    int i = 0;
    _tnode *top = NULL;
    int arr[] = {6,1,9,3,5,0,2,7};
    int count = sizeof(arr) / sizeof(arr[0]);
    for(i=0; i<count; i++) {
        top = add(top, arr[i], cmp_int);
        printf("add: %d\n", arr[i]);
    }

    print_tree(top);
    return 0;
}
4

1 に答える 1

5

基本的な考え方は次のとおりです。

挿入の場合、最初に、バランスの取れていないツリーの場合とまったく同じように、新しいノードを葉に挿入します。

次に、ルートに向かってツリーを上っていきます。各ノードについて、左右のサブツリーの高さの差が 1 を超えないようにします。

そうであれば、差1 以下になるようにノードを「ローテーション」します。たとえば、次のツリーを考えてみましょう。追加する前32はバランスが取れていましたが、現在はバランスが取れていません。

      128
     /
   64
  /
32

32両方のサブツリーの深さがゼロであるため、ノードでの深さの差はゼロです。

64左側のサブツリーの深さは 1 で、右側のサブツリーの深さは 0 であるため、ノードでの深さの差分は 1 です。

左側のサブツリーの深さは 2 で、右側のサブツリーの深さは 0であるため、128ノードでの深さの差は2です。そのため、そのノードを介したローテーションが発生する必要があります。これは128、 を右のサブツリーに押し下げ、 を起動することで実行でき64ます。

   64
  /  \
32    128

そしてあなたは再びバランスを取り戻します。

もちろん、回転方向は、高さが左または右に大きすぎるかどうかによって異なります。


削除は、挿入のように必ずしも葉ノードで作業しているわけではないため、少し注意が必要です。ノードに子がない (リーフである) か、1 つの子があるか、または 2 つの子があるかによって異なるため、少し複雑になります。

1/ リーフ ノードの場合は、それを削除してから、そのリーフの親でリバランスを開始できます。

2/ 子が 1 つのノードの場合、子情報 (データリンク) をコピーして、削除したいものを置き換えることができます。その後、子を削除し、子情報が現在ある場所で再調整を開始します。

3/ 2 つの子ノードの場合、アイデアは、その直後の後続ノードを見つけることです (最初に右の子に移動し、次に左の子がなくなるまで継続的に左の子に移動します)。同様に機能する直前の前任者 (左、連続して右) を見つけることもできます。

次に、削除するノードのデータを後続 (または先行) のデータと交換し、削除するノードがリーフ ノードになるまでこのルールを再適用します。次に、上記の (1) とまったく同じルールを使用して、そのリーフ ノードを削除します。

このスワッピングのトリックは完全に有効です。なぜなら、スワップによって 2 つの隣接するアイテムが一時的に順番がずれても、そのうちの 1 つ (2この場合) を削除しているという事実により、自動的に魔法のように修正されるからです。

  2           3           3
 / \   -->   / \   -->   /
1   3       1   2       1
=====       =====       =====
1,2,3       1,3,2       1,3
于 2015-06-03T04:26:49.423 に答える