AVL树的插入、删除、查找操作
2017-01-18 14:53
381 查看
AVL树是一种带有平衡条件的二叉查找树,所有节点都是基于平衡的。所谓平衡,即某个节点的左右子树的高度差最多相差1。空树的高度定义为-1。
下面我们通过如下两棵树(图片来自《数据结构与算法分析–C语言描述》)进一步了解怎样的树才是AVL树。
上图只有左边的树才是AVL树,那么下面我们来分析一下左边的树
(某个节点的高 = MAX(子树的高, 右子树的高 )+ 1)
节点3的高为0
节点4的高为1
节点1的高为0
节点7的高为0
节点2的高为2
节点8的高为1
节点5的高为3
你可以看到任一节点的左右子树的差都没有超过2,那么我们再看看右边的树
节点2的高为2
节点8的高为0
所以对于节点7来说,左右子树的高度差相差为2 > 1,失去平衡。
下面我们接着来说AVL的插入、删除、查找操作。
插入和删除操作都有可能导致AVL树的不平衡(为什么呢?原来的树是平衡的,即某个节点的左右子树的高度差小于2,假设左子树的高为1,右子树的高为0,如果这个时候,在这个节点的左子树插入一个节点,那么该节点的左右子树的高度差就为2了,删除操作也是一样的)。
对于出现不平衡的时候,需要通过旋转来解决。下面我们直接来看看插入操作。
我们把需要重新平衡的节点叫做A,那么出现不平衡的情况,会有下面四种:
1. 对A节点的左儿子的左子树进行插入
2. 对A节点的左儿子的右子树进行插入
3. 对A节点的右儿子的左子树进行插入
4. 对A节点的右儿子的右子树进行插入
下图对应上面四种情况
针对情况一和情况四,我们采取的是单旋转(旋转一次)的操作
针对情况二和情况三,我们采取的是双旋转(旋转两次)的操作
单旋转,其法则是,如果哪边偏高,就往另一边旋转,
如情况一,旋转后的效果如下图
情况四旋转后的效果如下图
下图(来自《数据结构与算法分析–C语言描述》)对应情况一,从图中可以看出,K2的左右节点的高度差为2,不平衡,左边偏高,所以要往右边旋转。
我们直接来看看该单旋转算法的实现
双旋转,先旋转失去平衡节点K3的第一个儿子节点K1,然后再旋转K3。如情况二,旋转后的最终效果如下图
情况三,旋转后的最终效果如下图
下图(来自《数据结构与算法分析–C语言描述》)对应情况二,从图上可以看出,K3节点的左右子树高度差为2,不平衡,需要先旋转K1和K2,然后再旋转K2和K3。
我们来看看该双旋转算法的实现
1. 如果删除的value比tree->value大,那么继续往右边搜索
2. 如果删除的value比tree->value小,那么继续往左边搜索
3. 如果找到删除的value,那么又要分以下两种情况处理
1. 如果该节点有左右孩子的,那么用右子树的最小节点替代这个被删除的节点,然后再递归删除右子树的最小节点
2. 如果该节点只有一个左或者右孩子或者是没有孩子节点的,直接删除,返回相应的tree指针
删除操作,也是有可能造成树的不平衡。比如,删除下面这棵树的节点4,就会引起节点3的不平衡(左孩子的高为2,右孩子的高为0)
有一个很重要的一点,删除之前,树是平衡的,所以删除之后,不管删除节点有几个孩子,其左右子节点的高度差最多是2,不会超过2(删除节点的父节点或者是父父节点,再往上也是一样的),否则原来的树就是不平衡的,删除之后,无非有可能出现下面这四种情况
当删除之后,先要更新tree的高度,然后再判断tree是否失去平衡,如果失去平衡,再按照下面的算法调整
1. 当查找value比tree->value小的时候,继续往左边查找
2. 当查找value比tree->value大的时候,继续往右边查找
3. 当查找value == tree->value时,返回
下面是上述操作的实现代码(下面代码有用到tree_visual_create.h,该头文件是将二叉树可视化,方便查看结果,具体可以参考二叉树的可视化)
avl_tree.h
avl_tree.c
下面我们通过如下两棵树(图片来自《数据结构与算法分析–C语言描述》)进一步了解怎样的树才是AVL树。
上图只有左边的树才是AVL树,那么下面我们来分析一下左边的树
(某个节点的高 = MAX(子树的高, 右子树的高 )+ 1)
节点3的高为0
节点4的高为1
节点1的高为0
节点7的高为0
节点2的高为2
节点8的高为1
节点5的高为3
你可以看到任一节点的左右子树的差都没有超过2,那么我们再看看右边的树
节点2的高为2
节点8的高为0
所以对于节点7来说,左右子树的高度差相差为2 > 1,失去平衡。
下面我们接着来说AVL的插入、删除、查找操作。
插入和删除操作都有可能导致AVL树的不平衡(为什么呢?原来的树是平衡的,即某个节点的左右子树的高度差小于2,假设左子树的高为1,右子树的高为0,如果这个时候,在这个节点的左子树插入一个节点,那么该节点的左右子树的高度差就为2了,删除操作也是一样的)。
对于出现不平衡的时候,需要通过旋转来解决。下面我们直接来看看插入操作。
插入操作
插入节点==》有可能失去平衡==》通过旋转操作来使树达到平衡我们把需要重新平衡的节点叫做A,那么出现不平衡的情况,会有下面四种:
1. 对A节点的左儿子的左子树进行插入
2. 对A节点的左儿子的右子树进行插入
3. 对A节点的右儿子的左子树进行插入
4. 对A节点的右儿子的右子树进行插入
下图对应上面四种情况
针对情况一和情况四,我们采取的是单旋转(旋转一次)的操作
针对情况二和情况三,我们采取的是双旋转(旋转两次)的操作
单旋转,其法则是,如果哪边偏高,就往另一边旋转,
如情况一,旋转后的效果如下图
情况四旋转后的效果如下图
下图(来自《数据结构与算法分析–C语言描述》)对应情况一,从图中可以看出,K2的左右节点的高度差为2,不平衡,左边偏高,所以要往右边旋转。
我们直接来看看该单旋转算法的实现
Position * avl_tree_single_rotate_with_left(AVL_TREE * k2) { Position * k1; k1 = k2->lChild; k2->lChild = k1->rChild; k1->rChild = k2; k2->height = avl_tree_max(avl_tree_height(k2->lChild), avl_tree_height(k2->rChild)) + 1; k1->height = avl_tree_max(avl_tree_height(k1->lChild), avl_tree_height(k1->rChild)) + 1; return k1; }
双旋转,先旋转失去平衡节点K3的第一个儿子节点K1,然后再旋转K3。如情况二,旋转后的最终效果如下图
情况三,旋转后的最终效果如下图
下图(来自《数据结构与算法分析–C语言描述》)对应情况二,从图上可以看出,K3节点的左右子树高度差为2,不平衡,需要先旋转K1和K2,然后再旋转K2和K3。
我们来看看该双旋转算法的实现
Position * avl_tree_double_rotate_with_left(AVL_TREE * k3) { /* Rotate between k1 and k2 */ k3->lChild = avl_tree_single_rotate_with_right(k3->lChild); /* Rotate between k2 and k3 */ return avl_tree_single_rotate_with_left(k3); }
删除操作
删除操作可以参考二叉查找树的删除操作,其实就是分以下三种情况1. 如果删除的value比tree->value大,那么继续往右边搜索
2. 如果删除的value比tree->value小,那么继续往左边搜索
3. 如果找到删除的value,那么又要分以下两种情况处理
1. 如果该节点有左右孩子的,那么用右子树的最小节点替代这个被删除的节点,然后再递归删除右子树的最小节点
2. 如果该节点只有一个左或者右孩子或者是没有孩子节点的,直接删除,返回相应的tree指针
删除操作,也是有可能造成树的不平衡。比如,删除下面这棵树的节点4,就会引起节点3的不平衡(左孩子的高为2,右孩子的高为0)
有一个很重要的一点,删除之前,树是平衡的,所以删除之后,不管删除节点有几个孩子,其左右子节点的高度差最多是2,不会超过2(删除节点的父节点或者是父父节点,再往上也是一样的),否则原来的树就是不平衡的,删除之后,无非有可能出现下面这四种情况
当删除之后,先要更新tree的高度,然后再判断tree是否失去平衡,如果失去平衡,再按照下面的算法调整
/* Update the height of tree */ tree->height = avl_tree_max(avl_tree_height(tree->lChild), avl_tree_height(tree->rChild)) + 1; if(avl_tree_height(tree->lChild) - avl_tree_height(tree->rChild) == 2) { if(tree->lChild->rChild) tree = avl_tree_double_rotate_with_left(tree); else tree = avl_tree_single_rotate_with_left(tree); } else if(avl_tree_height(tree->rChild)- avl_tree_height(tree->lChild) == 2) { if(tree->rChild->lChild) tree = avl_tree_double_rotate_with_right(tree); else tree = avl_tree_single_rotate_with_right(tree); }
查找操作
这个最简单,主要有三种情况:1. 当查找value比tree->value小的时候,继续往左边查找
2. 当查找value比tree->value大的时候,继续往右边查找
3. 当查找value == tree->value时,返回
下面是上述操作的实现代码(下面代码有用到tree_visual_create.h,该头文件是将二叉树可视化,方便查看结果,具体可以参考二叉树的可视化)
avl_tree.h
#ifndef __AVL_TREE_H__ #define __AVL_TREE_H__ typedef int ElementType; typedef struct AVL_TREE_T { ElementType value; struct AVL_TREE_T * lChild; struct AVL_TREE_T * rChild; int height; }AVL_TREE; typedef AVL_TREE Node; typedef AVL_TREE Position; extern void avl_tree_main(void); #endif
avl_tree.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "avl_tree.h"
#include "tree_visual_create.h"
int avl_tree_max(int h1, int h2)
{
if(h1 > h2)
return h1;
else
return h2;
}
int avl_tree_height(AVL_TREE * tree)
{
if(NULL == tree)
return -1;
else
return tree->height;
}
Node * avl_tree_find(AVL_TREE * tree, ElementType value)
{
Node * node = NULL;
if(tree)
{
if(tree->value == value)
node = tree;
else if(tree->value > value)
node = avl_tree_find(tree->lChild, value);
else if(tree->value < value)
node = avl_tree_find(tree->rChild, value);
}
return node;
}
Node * avl_tree_find_min(AVL_TREE * tree)
{
AVL_TREE * node = NULL;
if(tree)
{
if(tree->lChild)
node = avl_tree_find_min(tree->lChild);
else
node = tree;
}
return node;
}
Node * avl_tree_find_max(AVL_TREE * tree)
{
AVL_TREE * node = NULL;
if(tree)
{
if(tree->rChild)
node = avl_tree_find_max(tree->rChild);
else
node = tree;
}
return node;
}
Position * avl_tree_single_rotate_with_left(AVL_TREE * k2) { Position * k1; k1 = k2->lChild; k2->lChild = k1->rChild; k1->rChild = k2; k2->height = avl_tree_max(avl_tree_height(k2->lChild), avl_tree_height(k2->rChild)) + 1; k1->height = avl_tree_max(avl_tree_height(k1->lChild), avl_tree_height(k1->rChild)) + 1; return k1; }
Position * avl_tree_single_rotate_with_right(AVL_TREE * k1)
{
Position * k2;
k2 = k1->rChild;
k1->rChild = k2->lChild;
k2->lChild = k1;
k1->height = avl_tree_max(avl_tree_height(k1->lChild), avl_tree_height(k1->rChild)) + 1;
k2->height = avl_tree_max(avl_tree_height(k2->lChild), avl_tree_height(k2->rChild)) + 1;
return k2;
}
Position * avl_tree_double_rotate_with_left(AVL_TREE * k3) { /* Rotate between k1 and k2 */ k3->lChild = avl_tree_single_rotate_with_right(k3->lChild); /* Rotate between k2 and k3 */ return avl_tree_single_rotate_with_left(k3); }
Position * avl_tree_double_rotate_with_right(AVL_TREE * k1)
{
/* Rotate between k2 and k3 */
k1->rChild = avl_tree_single_rotate_with_left(k1->rChild);
/* Rotate between k1 and k2 */
return avl_tree_single_rotate_with_right(k1);
}
AVL_TREE * avl_tree_insert(AVL_TREE * tree, ElementType value)
{
if(NULL == tree)
{
tree = (AVL_TREE *)malloc(sizeof(AVL_TREE));
if(NULL == tree)
printf("AVL_TREE malloc failed\n");
else
{
tree->value = value;
tree->height = 0;
tree->lChild = tree->rChild = NULL;
}
}
else if(tree->value > value)
{
tree->lChild = avl_tree_insert(tree->lChild, value);
if(avl_tree_height(tree->lChild) - avl_tree_height(tree->rChild) == 2)
{
if(value < tree->lChild->value)
tree = avl_tree_single_rotate_with_left(tree);
else
tree = avl_tree_double_rotate_with_left(tree);
}
}
else if(tree->value < value)
{
tree->rChild = avl_tree_insert(tree->rChild, value);
if(avl_tree_height(tree->rChild)- avl_tree_height(tree->lChild) == 2)
{
if(value > tree->rChild->value)
tree = avl_tree_single_rotate_with_right(tree);
else
tree = avl_tree_double_rotate_with_right(tree);
}
}
tree->height = avl_tree_max(avl_tree_height(tree->lChild), avl_tree_height(tree->rChild)) + 1;
return tree;
}
Node * avl_tree_delete(AVL_TREE * tree, ElementType value)
{
AVL_TREE * temp = NULL;
if(NULL == tree)
{
printf("Not found the element\n");
}
else if(value > tree->value) /* Go Right */
{
tree->rChild = avl_tree_delete(tree->rChild, value);
}
else if(value < tree->value) /* Go Left */
{
tree->lChild = avl_tree_delete(tree->lChild, value);
}
else if(tree->lChild && tree->rChild) /* Two Children */
{
temp = avl_tree_find_min(tree->rChild);
tree->value = temp->value;
tree->rChild = avl_tree_delete(tree->rChild, tree->value);
}
else /* one or zero Children */
{
temp = tree;
if(NULL == tree->lChild)
{
tree = tree->rChild;
}
else if(NULL == tree->rChild)
{
tree = tree->lChild;
}
free(temp);
}
if(tree) /* Ignore the free node */
{
/* Update the height of tree */
tree->height = avl_tree_max(avl_tree_height(tree->lChild), avl_tree_height(tree->rChild)) + 1;
if(avl_tree_height(tree->lChild) - avl_tree_height(tree->rChild) == 2)
{
if(tree->lChild->rChild)
tree = avl_tree_double_rotate_with_left(tree);
else
tree = avl_tree_single_rotate_with_left(tree);
}
else if(avl_tree_height(tree->rChild)- avl_tree_height(tree->lChild) == 2)
{
if(tree->rChild->lChild)
tree = avl_tree_double_rotate_with_right(tree);
else
tree = avl_tree_single_rotate_with_right(tree);
}
}
return tree;
}
void avl_tree_destroy(AVL_TREE * tree)
{
if(tree)
{
if(tree->lChild)
avl_tree_destroy(tree->lChild);
if(tree->rChild)
avl_tree_destroy(tree->rChild);
free(tree);
}
}
void avl_tree_main(void)
{
AVL_TREE * tree = NULL;
//int i=0;
//for(i=1; i<=127; i++)
// tree = avl_tree_insert(tree, i);
#if 0
tree = avl_tree_insert(tree, 7);
tree = avl_tree_insert(tree, 6);
tree = avl_tree_insert(tree, 10);
tree = avl_tree_insert(tree, 5);
tree = avl_tree_insert(tree, 13);
tree = avl_tree_insert(tree, 8);
tree = avl_tree_insert(tree, 11);
#endif
#if 0
tree = avl_tree_insert(tree, 5);
tree = avl_tree_insert(tree, 3);
tree = avl_tree_insert(tree, 6);
tree = avl_tree_insert(tree, 4);
#endif
#if 0
tree = avl_tree_insert(tree, 5);
tree = avl_tree_insert(tree, 2);
tree = avl_tree_insert(tree, 8);
tree = avl_tree_insert(tree, 1);
tree = avl_tree_insert(tree, 4);
tree = avl_tree_insert(tree, 7);
tree = avl_tree_insert(tree, 3);
#endif
#if 0
tree = avl_tree_insert(tree, 7);
tree = avl_tree_insert(tree, 6);
tree = avl_tree_insert(tree, 8);
tree = avl_tree_insert(tree, 5);
tree = avl_tree_insert(tree, 9);
tree = avl_tree_insert(tree, 10);
#endif
#if 0
tree = avl_tree_insert(tree, 8);
tree = avl_tree_insert(tree, 3);
tree = avl_tree_insert(tree, 10);
tree = avl_tree_insert(tree, 2);
tree = avl_tree_insert(tree, 5);
tree = avl_tree_insert(tree, 9);
tree = avl_tree_insert(tree, 11);
tree = avl_tree_insert(tree, 1);
tree = avl_tree_insert(tree, 4);
tree = avl_tree_insert(tree, 6);
tree = avl_tree_insert(tree, 12);
tree = avl_tree_insert(tree, 7);
#endif
#if 1
tree = avl_tree_insert(tree, 3);
tree = avl_tree_insert(tree, 2);
tree = avl_tree_insert(tree, 4);
tree = avl_tree_insert(tree, 1);
#endif
tree_visual_create(tree, "tree_insert.dot");
Node * min = avl_tree_find_min(tree);
printf("min value : %d\n", min->value);
Node * max = avl_tree_find_max(tree);
printf("max value : %d\n", max->value);
Node * find = avl_tree_find(tree, 2);
if(find)
printf("find value : %d\n", find->value);
else
printf("Cannot find the value\n");
tree = avl_tree_delete(tree, 4);
tree_visual_create(tree, "tree_delete.dot");
avl_tree_destroy(tree);
}
相关文章推荐
- 结构之美:线性表的查找、插入与删除操作
- AVL树的查找,插入,删除
- 二叉查找树的操作(插入、删除、查找)
- Trie 建立、插入、查找、删除操作
- 单链表操作(建表、插入、删除、查找、求表长),
- 跳表(Skip List)的介绍以及查找插入删除等操作
- 跳表(Skip List)的介绍以及查找插入删除等操作
- 单链表-创建、插入、删除、查找、反转等操作
- [C++]数据结构:平衡的二叉搜索树之AVL树的结构特点与基础插入删除操作
- 链表操作:创建,插入,删除,查找等功能
- 顺序表的各种操作(建立,查找,删除,插入等)
- 使用JAVA代码来模拟线性链表的相关操作(增加,删除,插入及查找等)
- 关于动态存储分配函数的调用,在已经过排序的数组中查找及删除内容的操作,余数的分析,删除字符数组中的空格,对链表的逆置,在源字符串中查找子字符串的个数,函数指针以及函数的调用,循环赋值带来的问题以及插入
- 单链表相关操作之C语言实现:插入,删除,倒转,复制,查找。。。
- AVL树的插入_删除操作实现~
- 单链表建立,插入,删除,查找,遍历操作
- 单链表的创建、插入,删除、查找等操作
- 单链表 建立, 查找, 删除, 插入 操作
- 数据结构学习(三)——单链表的操作之查找、删除、插入。
- 单链表建立,插入,删除,查找,遍历操作