前言
在进行学习的过程中查阅了很多资料,这些资料中主要有以下几个问题:
- 树的节点高度获取方式不同:
(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;
}
节点删除
删除节点需要注意的几点:
- 删除双子节点的节点值需要考虑两个子节点的高度,使用较高的节点的最右(子左节点高)或者最左节点(子右节点高)的值替换当前节点的val。左子节点高的情况下,如果子节点的最右子节点存在左枝,将左枝给最右子节点的父节点的右枝;右子节点高的情况下,相反。
- 只有一个子节点或者没有,直接返回左或者右节点即可。
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请直接开喷,让我涨涨记性。