如何进行二叉树平衡

时间:2015-06-03 04:20:32

标签: c algorithm binary-tree tree-balancing

这是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;
}

1 个答案:

答案 0 :(得分:5)

基本思路如下。

对于插入,您首先在叶子上插入新节点,就像对非平衡树一样。

然后沿着树向上朝向根工作,确保对于每个节点,左右子树之间的高度差异绝不会超过一个。

如果是的话,你&#34;旋转&#34;节点使的差值为1或更小。例如,考虑以下树,在添加32之前平衡了,但现在不是

      128
     /
   64
  /
32

32节点的深度差为零,因为两个子树的深度均为零。

64节点的深度差为1,因为左子树的深度为1,右子树的深度为零。

128节点的深度差为 2 ,因为左子树的深度为2,右子树的深度为零。因此需要通过该节点进行旋转。这可以通过将128向下推到右侧子树并调出64来完成:

   64
  /  \
32    128

你又一次有了平衡。

旋转的方向当然取决于左侧或右侧的高度是否过高。

删除有点棘手,因为您不一定像插入插件一样在叶子节点上工作。它有点复杂,因为它取决于节点是否没有子节点(是叶子),一个孩子或两个孩子。

1 /对于叶节点,您可以删除它,然后在该叶子的父节点处开始重新平衡。

2 /对于一个子节点,您只需复制子信息(数据链接)即可替换您要删除的子信息,然后删除子节点并开始重新平衡孩子的信息现在是。

3 /对于一个双子节点,我们的想法是找到它的直接继承者(首先去找右边的孩子,然后继续向左边的孩子走,直到没有剩下的孩子为止)。你也可以找到它的直接前身(左边然后连续右边)也可以。

然后,将要删除的节点中的数据与后继(或前任)中的数据交换,然后重新应用此规则,直到要删除的节点为叶节点。然后使用与上面(1)完全相同的规则删除该叶节点。

这个交换技巧是完全有效的,因为即使交换将两个相邻的项暂时不按顺序排列,你还要删除其中一个(在这种情况下为2)这一事实会自动修复事情:

  2           3           3
 / \   -->   / \   -->   /
1   3       1   2       1
=====       =====       =====
1,2,3       1,3,2       1,3