【c语言数据结构之二叉平衡树avl树】

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


typedef struct TreeNode{
    int height;
//  在结构体中添加一个高度成员变量,以在每次插入时候都可以检查相对根节点的是否平衡
    int data;
    struct TreeNode * lchild;
    struct TreeNode * rchild;
}TreeNode;

int get_height(TreeNode * node){
//  之所以需要这个函数,是因为避免调node->height结果出现空指针异常
//  在每一次插入后,相对根节点都需要更新它的高度值
    return node?node->height:0;
//  如果结点为空则高度值为0,在最下面的结点的高度值都为1
}

int get_max(int a, int b){
    return a>b? a: b;
//  在每一次插入后,相对根节点都需要更新它的高度值,当然递归返回之后,你也不知道插入的元素跑下
//  面哪去了,所以更新相对根节点的高度值时候,找左右儿子的高度差,一旦达到2,就说明刚刚那次插入
//  使这个局部位置失衡了,那从相对根节点开始往下看,无非三个结点需要重新移动位置
//  局部失衡有几种情况呢?
//  四种,三个结点位置从上往下看分别是
//  1.    a     2.  a     3. a        4.   a
//       b         b           b             b
//      c           c            c         c
//  说说调整思路
//  1 和 3很简单,分别调整一次就可以,调整的方式就是旋转,分别是顺时针旋转和逆时针旋转
//  1 直接顺指针转 转成这样    b        3 直接逆时针旋转 转成这样    b
//                         c   a                               a   c
//  ok,顺便说一下这两个旋转的代码
}

void rr_rotation(TreeNode* node, TreeNode** root) {
    /**
     *   a
     *    b
     *     c
     */
//   这就是逆时针旋转,需要传入的参数当然是这个小局部的相对根结点,相对上图情况就是a结点的位置
//   所以需传入存放a结点位置的位置,因为要改变这个位置的内容,使相对根节点得到真正的改变,root
//   当然还要传入a结点的位置,通过该位置(指针)改变a结点结构体中的左右子指针以及高度,node
     *root = node->rchild;
//   原相对根节点位置存放的是a的位置,现在直接改成b的位置,b正式成为这个局部的相对根节点
     node->rchild = NULL;
//   a的右儿子本来是b,现在当然不是了,a要去做b的儿子了,a的左儿子本来就一定为空,故不画蛇添足
//   但右儿子需重新赋值为0
     node->height = 1;
//   高度当然,本来为3,但现在为1
//   多提一嘴,新的根节点b的高度这不用设置,因为每次插入函数的最末尾都会更新高度
//   在更新高度前,会检验是否出现失衡,当然这里的函数会在插入函数的中间位置一旦发现失衡才会调用
//   所以调用完,相对根结点的高度一定会更新,这就不重复了!
     (*root)->lchild = node;
//   ok,a变成b的儿子,c其实什么都没改变(这也是后续两步调整中的铺垫)
//   经过调整变成,解决了这个局部的失衡
    /**
     *     b
     *   a   c
     */
}

void ll_rotation(TreeNode* node, TreeNode** root) {
    //      大同小异!
    /**
     *       a
     *     b
     *   c
     */
     *root = node->lchild;
     node->lchild = NULL;
     node->height = 1;
     (*root)->rchild = node;
    /**
     *     b
     *   c   a
     */
}
// 这里说说这种情况:    a
//                       b
//                     c
// 这种情况就要分两步调整,首先,先以b为相对根节点,对 bc这个局部进行调整,你可以假设c有个左孩子
// 实际这样假设对调整结果当然没有影响,但这样可以直接对bc这个局部进行ll调整,
// 经过调整: a
//             c                                                                          
//               b
// 好了简单,第二步,以a为相对根节点看这个局部,直接rr旋转调整
// 最终结果:
//           c
//         a   b 
// 解决了这种情况的失衡,当然 
// 情况如   a
//        b
//          c
// 先以b为局部根节点,对bc这个局部进行rr旋转调整,然后再以a为相对根进行ll旋转调整
// ok,做完所有铺垫,来到了真正的avl插入,我总结为边插入,边检查,检查到了就转转
    

void avl_insert(TreeNode ** root, int data){
    if((*root) == NULL){
//  碰到空位置插入,很好理解
        (*root) = (TreeNode *) malloc(sizeof(TreeNode));
        (*root)->data = data;
        (*root)->lchild = NULL;
        (*root)->rchild = NULL;
//   赋空,当然也是为后来的元素插入留位置,毕竟,看到空位置才插入
        (*root)->height = 1;
//   一旦屁股坐定(一定是在最下端,当然高度是1)
    }else{
        if((*root)->data < data){
//   这就是排序树的插入规则了,比根节点大的数据,往右找空位,反之亦然
            avl_insert(&((*root)->rchild), data);
//   利用递归去寻找位置并插入,接下来就是插完之后必须检查了!
            int r_height = get_height((*root)->rchild);
            int l_height = get_height((*root)->lchild);
            if(r_height - l_height == 2){
//   左右高度相差2时,必须调整失衡
                if(data < (*root)->rchild->data)
//   这种情况:  a 
//                b
//              c

                {
//   简单,先以b为相对根对bc局部进行ll旋转,再对abc以a为相对根进行rr旋转
                    ll_rotation((*root)->rchild, &((*root)->rchild));
                    rr_rotation((*root), &(*root));
                }else{
//   这种就是简单的rr旋转了
                    rr_rotation((*root), &(*root));
                }
            }
        }else if((*root)->data > data){
//   下面的当然是ll或者先rr再ll两步走
            avl_insert(&((*root)->lchild), data);
            int r_height = get_height((*root)->rchild);
            int l_height = get_height((*root)->lchild);
            if(l_height - r_height == 2){
                if(data > (*root)->lchild->data)
                {
                    rr_rotation((*root)->lchild, &((*root)->lchild));
                    ll_rotation((*root), &(*root));
                }else{
                    ll_rotation((*root), &(*root));
                }
            }
        }
//    很帅也很关键的一步,每一次插入,虽然每次插入后要检查平衡,必要时调整
//    高度当然也要进行更新,每次插入都要更新!会以递归的形式进行更新,试着调试一下
//    逻辑美如画~
        (*root)->height = get_max(get_height((*root)->lchild), get_height((*root)->rchild)) + 1;
    }
}

void pre_order(TreeNode * node){
//  先序遍历
    if(node == NULL)
        return;
    printf("%d ", node->data);
    pre_order(node->lchild);
    pre_order(node->rchild);
}

typedef struct Queue{
//  层次遍历需要一个先进先出的队列
    TreeNode *data;
    struct Queue * next;
}Queue;

Queue * init_queue(){
    Queue * head = (Queue *) malloc(sizeof(Queue));
    head->data = NULL;
    head->next = NULL;
}

int get_length(Queue * head){
    int length = 0;
    head = head->next;
    while (head){
        length++;
        head = head->next;
    }
    return length;
}

void en_queue(Queue * head, TreeNode * node){
    Queue * last_q_node = head;
    int length = get_length(head);
    for (int i = 0; i < length; ++i) {
        last_q_node = last_q_node->next;
    }
    Queue * en_q_node = (Queue *) malloc(sizeof(Queue));
    en_q_node->data = node;
    last_q_node->next = en_q_node;
    en_q_node->next = NULL;
}

TreeNode * de_queue(Queue * head){
    Queue * de_q_node = head->next;
    TreeNode * de_q_node_data = de_q_node->data;
    head->next = de_q_node->next;
    free(de_q_node);
    return de_q_node_data;
}

void print_queue(Queue * head){
    int length = get_length(head);
    printf("head, front --- back\n");
    head = head->next;
    for (int i = 0; i < length; ++i) {
        printf("%d ",head->data->data);
        head = head->next;
    }
    printf("\n");
}

void level_traversal(TreeNode * root){
    Queue * head = init_queue();
    en_queue(head, root);
    while(get_length(head) != 0){
        TreeNode * de_node = de_queue(head);
        printf("%d, height = %d\n",de_node->data, de_node->height);
        if(de_node->lchild){
            en_queue(head, de_node->lchild);
        }
        if(de_node->rchild){
            en_queue(head, de_node->rchild);
        }
    }
}


int main() {
    int num[5] = {5,3,4,1,6}; // lr rr+ll
    /**
     *           5
     *         3
     *           4
     *           rr
     *          5
     *         4
     *        3
     *
     *           4
     *         3   5
     *       1      6
     */
    TreeNode * root = NULL;
    for (int i = 0; i < 5; ++i) {
        avl_insert(&root, num[i]);
    }
    level_traversal(root);
//  层次遍历之后应该是: 4 3 5 1 6
    return 0;
}

运行结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值