AVL树的插入、删除C语言实现

前言

在进行学习的过程中查阅了很多资料,这些资料中主要有以下几个问题:

  1. 树的节点高度获取方式不同:
    (1) 在树节点内定义一个变量储存节点高度,在进行旋转等操作的时候修改节点高度,优点:使用方便,缺点:在进行插入、删除操作时需子节点的高度无法修改,只能修改当前及其父节点的高度,且自我感觉这种方式定义节点高度存在计算错误。
    (2) 在用到节点时,调用函数计算树的高度。
    相比于(1)的方法,(2)方法更加直观,容易理解。

AVL树的实现

树节点定义与获取

节点定义

typedef struct Node{
    int val;
    struct Node * left;
    struct Node * right;
}TreeNode;

节点获取

TreeNode* getTreeNode(int val) {
    TreeNode *node = (TreeNode*)malloc(sizeof(TreeNode));
    if(!node) return NULL;
    node->val = val;
    node->left = NULL;
    node->right = NULL;
    return node;
}

树的节点高度获取

叶节点的高度取值是0还是1没有统一的定论,可以参考维基百科上面的定义,但具体使用不影响。

int getTreeHeight(TreeNode* tree) {
    if(!tree) return 0;
    int hl = getTreeHeight(tree->left);
    int hr = getTreeHeight(tree->right);
    return (hl>hr?hl:hr)+1;
}

节点的旋转

右旋

TreeNode* rightRotate(TreeNode* tree) { // 注意顺序,否则可能丢失节点
    TreeNode* tmp = tree->left;  // 右旋:原树根的左节点将成为新根
    tree->left = tmp->right;
    tmp->right = tree;
    return tmp;
}

左旋

TreeNode* leftRotate(TreeNode* tree) {
    TreeNode* tmp = tree->right;  // 左旋:树的右节点将成为新根
    tree->right = tmp->left;
    tmp->left = tree;    // 原树根成为新树根的左节点
    return tmp;
}

左右旋

TreeNode* leftRightRotate(TreeNode* tree) {
    tree->left = leftRotate(tree->left); // 注意是树的左节点
    return rightRotate(tree);
}

右左旋

TreeNode* rightLeftRotate(TreeNode* tree) {
    tree->right = rightRotate(tree->right);
    return leftRotate(tree);
}

节点的插入(树中节点值唯一)

TreeNode* balanceTree(TreeNode * tree, int insertVal) {
    int hl = getTreeHeight(tree->left);
    int hr = getTreeHeight(tree->right);
    if (hr>=hl+2) {
        if (insertVal>tree->right->val) tree = leftRotate(tree); else tree = rightLeftRotate(tree);
    }
    if (hl>=hr+2) {
        if (insertVal>tree->left->val) tree = leftRightRotate(tree); else tree = rightRotate(tree);
    }
    return tree;
}
TreeNode* insertTreeNode(TreeNode * tree, int insertVal) {
    if(!tree) return getTreeNode(insertVal);
    else if (insertVal > tree->val) {
        tree->right = insertTreeNode(tree->right, insertVal);
        return balanceTree(tree, insertVal);
    } else if (insertVal < tree->val){
        tree->left = insertTreeNode(tree->left, insertVal);
        return balanceTree(tree, insertVal);
    }
    return tree;
}

节点删除

删除节点需要注意的几点:

  1. 删除双子节点的节点值需要考虑两个子节点的高度,使用较高的节点的最右(子左节点高)或者最左节点(子右节点高)的值替换当前节点的val。左子节点高的情况下,如果子节点的最右子节点存在左枝,将左枝给最右子节点的父节点的右枝;右子节点高的情况下,相反。
  2. 只有一个子节点或者没有,直接返回左或者右节点即可。
TreeNode* deleteTreeNode(TreeNode* tree, int deleteVal) {
    if (!tree){ printf("Not find the Node whose value is %d", deleteVal); return tree; }
    if (deleteVal>tree->val) {
        tree->right = deleteTreeNode(tree->right, deleteVal);
        if (getTreeHeight(tree->left) - getTreeHeight(tree->right)>=2) {  //是否旋转

            if (getTreeHeight(tree->left->left)>getTreeHeight(tree->left->right)) {  // 旋转形式判定
                tree =  rightRotate(tree);
            } else {
                tree = leftRightRotate(tree);
            }
        }
    } else if (deleteVal<tree->val) {
        tree->left = deleteTreeNode(tree->left, deleteVal);
        if (getTreeHeight(tree->right)-getTreeHeight(tree->left)>=2) {
            if (getTreeHeight(tree->right->right)>getTreeHeight(tree->right->left)) {
                return leftRotate(tree);
            } else {
                return rightLeftRotate(tree);
            }
        }
    } else {
        if (tree->left && tree->right) {
            int hl = getTreeHeight(tree->left), hr = getTreeHeight(tree->right);
            if (hl>hr) {
                TreeNode* tmpFather = tree->left;
                TreeNode* tmp = tree->left->right;
                if (!tmp) {
                    tmpFather->right = tree->right;
                    free(tree);
                    return tmpFather;// 重点检查
                } else {
                    while (tmp->right) {  //查找最右子节点
                        tmpFather = tmp;
                        tmp = tmp->right;
                    }
                    tree->val = tmp->val;
                    tmpFather->right = tmp->left ? tmp->left : NULL; //最右子节点是否有左子树(只有两种情况:左子树存在和不存在)
                    return tree;
                }
            } else {
                TreeNode* tmpFather = tree->right;
                TreeNode* tmp = tmpFather->left;
                if (!tmp) {
                    tmpFather->left = tree->left;
                    free(tree);
                    return tmpFather;
                } else {
                    while (tmp->left) {  // 与上相同
                        tmpFather = tmp;
                        tmp = tmp->left;
                    }
                    tree->val = tmp->val;
                    tmpFather->left = tmp->right ? tmp->right : NULL;
                    return tree;
                }
            }
        } else {
            return tree->left?tree->left:tree->right;
        }

    }
    return tree;
}

节点遍历

前序遍历

void preOrder(TreeNode* tree) {
    if (!tree) return;
    printf(" %d", tree->val);
    preOrder(tree->left);
    preOrder(tree->right);
}

中序遍历

void inOrder(TreeNode* tree) {
    if (!tree) return;
    inOrder(tree->left);
    printf(" %d", tree->val);
    inOrder(tree->right);
}

删除树

为了避免内存泄漏,在使用完后需要将内存释放,释放必须从子节点开始。单纯释放根节点会造成子节点的内存无法释放。

void deleteTree(TreeNode* tree) {
    if (!tree) return ;
    deleteTree(tree->left);
    deleteTree(tree->right);
    free(tree);
}

每天都在写BUG,如果发现BUG请直接开喷,让我涨涨记性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值