二叉树

什么是二叉树?

父节点至多只有两个子树的树形结构成为二叉树。如下图所示,图1不是二叉树,图2是一棵二叉树。

          图1 普通的树                                                                                   图2 二叉树

如果一棵树所有的非叶子节点都有两个子节点,则称该树为完全二叉树,图2就是一棵完全二叉树。

二叉查找树(ADT

二叉树一个重要的应用是二差查找树,顾名思义,二叉查找树是二叉树在查找方面的应用。根据以往的知识,如果给你一个数组或者是链表,可能需要遍历一整个数组或者是量表才能找到需要查找的目标。使用二叉树作为查找的数据结构,能够大大缩小查找的深度(若链表和数组的长度为N,一棵二叉树的深度为logN),提高查找的效率。

如何定义一棵查找树?

设每一个节点对应一个键值,而该节点的左子节点对用的数值小于该节点的值,该节点的右子节点的数值大于该节点的值。如下图所示:

              图3 二差查找树                                                             图4 非二差查找树

图3是二叉查找树,因为他总数满足上述的条件,而图4不是二叉查找树,图中红色框住的部分不满足二叉查找树的条件(父节点6<左子节点7)。

代码:

struct Tree{

double value;

Tree *left;

Tree *right;

};

TreeNode表示二叉树节点。其中value表示树对应的值,left表示该节点的左节点指针,right表示该节点的右节点指针。

二叉查找树的重要操作

1.查找

1.1 查找固定的数值

给定一个数值,查找二叉树中是否有对应的数值,如果树中包含该数值,则返回数值对应的节点,否则,返回NULL。查找的过程:

首先定位到跟节点,如果查找的数值跟节点的数值相等,则根节点为所求;否则,如果数值小于根节点的数值,则查找根节点的左节点,相反,则查找跟节点的右节点。这样一直遍历下去,知道找到对应的或者或者已经不能继续往下查找为止(即已经到达子节点)。

代码:

 Tree* Find(double value,Tree *t){
if(t==NULL)
return NULL;
if(value==t->value)
return t;
else if(value<t->value)
return Find(value,t->left);
else
return Find(value,t->right);
}

1.2查找最大值或最小值

二叉查找树一个很大的特点是左子节点的数值<父节点的数值,而右子节点的数值>父节点的数值,因此查找最大值或者最小值就相当的方便,只需要从跟节点开始,不断遍历左子结点,直到到达叶子节点,就可以得到最小值;而查找最大值则从根节点开始遍历右子结点,直到到达叶子节点,对用的数值即为最大值。

代码:

超找最小元素的节点

 Tree* FindMin(Tree* t){
if(t==NULL)
return NULL;
if(t->left==NULL)
return t;
else{
return FindMin(t->left);
}
}

查找最大元素的节点

 Tree* FindMax(Tree *t){
if(t==NULL)
return NULL;
if(t->right==NULL)
return t->right;
else
return FindMax(t->right);
}

2. 插入

插入操作是将一个数值插入到二叉查找树中的过程,插入后的树依然满足二叉查找树的条件。一棵二叉查找树的构建过程,就是一个不断将元素插入到二叉树的过程。

插入的操作最关键的是一个查找的过程,如果在二叉树中找到要插入的数值,则什么都不用做,如果找不到,只需要在最后我们查找的叶子节点上新增加一个子节点即可。举一个例子:

上图是一棵二叉树,如果我们要插入11,则按以下步骤进行:

再比如要找17,按以下步骤查找:

代码:

 void Insert(double value,Tree* t){
if(t==NULL){
t = new Tree();
if(t==NULL)
return;
else{
t->value=value;
t->left=NULL;
t->right=NULL;
}
}
if(t->value>value)
Insert(value,t->left);
else
Insert(value,t->right);
}

3. 删除

删除操作就是删除键值与数值相同的节点。删除操作相比查找和插入来说是一个较为复杂的过程。如果要删除的节点就是叶子节点,直接将该节点删除即可,但是,如果要删除非叶子节点,就需要通过调整其他节点的位置来构建新的二叉树。一般来说,直接用该节点的右子树的最小值替换该节点的值,然后再删除右子树的最小节点即可。

例子:

AVL

虽说二叉查找树是一种优秀的数据结构,能够大大降低数据查询的复杂度。但是,并不是说有情况下二叉树都能够达到快速查找的目的。

我们发现,如果按照[7,10,11,12,14,15,18]这样的顺序一个个元素进行插入的话就会出现右图所示的二叉树,这样的二叉树跟一个链表几乎是没有区别的,查找的效率一样,没有体现出二叉树的优势。出现这种原因是构建二叉树的过程中没有平衡节点的左右子树的高度。根节点7的右子树有很高的深度,但是左子树是空的。我们需要的是一棵左右节点平衡的二叉树,而其中一种传统的平衡二叉树是AVL树。

定义:AVL树是二叉树,其各个节点的左右子树的高度相差不超过1。

定义:数的高度可以看做是节点与最低的叶子节点的距离。跟节点的高度最大,而叶子节点的高度为0,一个不存在的节点的高度定义为-1。

例如:左图中节点12的高度为2,节点18的高度为0,而右图中节点12的高度为3,节点18的高度为1。

左图是一棵AVL树,右图不是一棵AVL树,因为右图节点15的左右子树的高度相差2(左子树的高度为-1,右子树的盖度为1)。

AVL树的构建

AVL树的构建同样是不断将元素插入的过程,但是与二叉查找树不同,AVL树在插入的过程中需要满足AVL树的条件,如果发现插入新的元素后不能满足AVL条件,需要通过调整元素的位置直到满足条件。

元素的插入无非就只有以下四种情况:1.左子树插入一个左节点;2.左子树插入一个右节点;3.右子树插入一个左节点;4.右子树插入一个右节点。

其中,1和4、2和3是对称的操作,因此下面只讨论1和2两种情况。

1. 左子树插入一个左节点

如上图所述,左边是原始的二叉树,该二叉树满足AVL树的条件,在插入元素5后变成了右边的二叉树,而此时不满足AVL树的条件,因为节点10的左右两棵子树的高度相差2(左子树的高度为1,右子树的高度为-1)。

此时需要通过对子树进行调整才能让二叉树再次满足AVL的条件。

如上图所示,调整后的二叉树重新变成一棵AVL树,则种调整的方法称为“左旋转”,通过旋转调整节点的位置,使二叉树满足AVL树的条件。同理,如果新增的元素为右子树的右子树,而且新增后子树的左右子树高度相差为2,此时进行“右旋转”即可调整为AVL树。

2. 左子树插入一个右节点

如上图所述,左边是原始的二叉树,该二叉树满足AVL树的条件,在插入元素8后变成了右边的二叉树,而此时不满足AVL树的条件,因为节点10的左右两棵子树的高度相差2(左子树的高度为1,右子树的高度为-1)。

与情况1(左子树新增左节点)不一样,此时不能通过一个简单的“坐旋转”来调整左右子树的高度。怎么办呢?需要通过两次“旋转”,显示通过对元素7进行右旋转,然后再对10进行左旋转。如下图所示:

      同理,对于右子树插入一个左节点的情况,如果此时不符合AVL树的条件,需要先进性“左旋转”,再进行“右旋转”即可。

代码:

 AVLTree* AVLInsert(double value,AVLTree * tree){
if(tree==NULL){
tree = new AVLTree();
tree->value=value;
tree->left=NULL;
tree->right=NULL;
tree->height=;
return;
}
if(value>tree->value){
tree->right=AVLInsert(value,tree->right);
if(Height(tree->right)-Height(tree->left)==){
if(value>tree->right->value)
tree=SingleRotateWithRight(tree);
else
tree=DoubleRouteWithRight(tree);
}
}
else{
tree->left=AVLInsert(value,tree->left);
if(Height(tree->left)-Height(tree->right)==){
if(value<tree->left->value)
tree=SingleRotateWithLeft(tree);
else
tree=DoubleRouteWithLeft(tree);
}
}
tree->height = Height(tree->left)>Height(tree->right)?Height(tree->left)+:Height(tree->right)+;
return tree;
}
//左旋转
AVLTree* SingleRotateWithLeft(AVLTree * tree){
AVLTree * left = tree->left;
tree->left=left->right;
left->right=tree;
tree->height=Height(tree->left)>Height(tree->right)?Height(tree->left)+:Height(tree->right)+;
left->height=Height(left->left)>Height(left->right)?Height(left->left)+:Height(left->right)+;
return left;
}
//右旋转
AVLTree* SingleRotateWithRight(AVLTree * tree){
AVLTree * right = tree->right;
tree->right=right->left;
right->left=tree;
tree->height=Height(tree->left)>Height(tree->right)?Height(tree->left)+:Height(tree->right)+;
right->height=Height(right->left)>Height(right->right)?Height(right->left)+:Height(right->right)+;
return right;
}
//右—左双旋转
AVLTree* DoubleRouteWithLeft(AVLTree* tree){
tree->left = SingleRotateWithRight(tree->left);
return SingleRotateWithLeft(tree);
}
//右—左双旋转
AVLTree* DoubleRouteWithRight(AVLTree* tree){
tree->right = SingleRotateWithLeft(tree->right);
return SingleRotateWithRight(tree);
}

二叉树与AVL树的更多相关文章

  1. 二叉树,AVL树和红黑树

    为了接下来能更好的学习TreeMap和TreeSet,讲解一下二叉树,AVL树和红黑树. 1. 二叉查找树 2. AVL树 2.1. 树旋转 2.1.1. 左旋和右旋 2.1.2. 左左,右右,左右, ...

  2. 二叉树-二叉查找树-AVL树-遍历

    一.二叉树 定义:每个节点都不能有多于两个的儿子的树. 二叉树节点声明: struct treeNode { elementType element; treeNode * left; treeNod ...

  3. python常用算法(5)——树,二叉树与AVL树

    1,树 树是一种非常重要的非线性数据结构,直观的看,它是数据元素(在树中称为节点)按分支关系组织起来的结构,很像自然界中树那样.树结构在客观世界中广泛存在,如人类社会的族谱和各种社会组织机构都可用树形 ...

  4. 5分钟了解二叉树之AVL树

    转载请注明出处:https://www.cnblogs.com/morningli/p/16033733.html AVL树是带有平衡条件的二叉查找树,其每个节点的左子树和右子树的高度最多相差1.为了 ...

  5. 二叉树之AVL树的平衡实现(递归与非递归)

    这篇文章用来复习AVL的平衡操作,分别会介绍其旋转操作的递归与非递归实现,但是最终带有插入示例的版本会以递归呈现. 下面这张图绘制了需要旋转操作的8种情况.(我要给做这张图的兄弟一个赞)后面会给出这八 ...

  6. 二叉树之AVL树

    高度为 h 的 AVL 树,节点数 N 最多2^h − 1: 最少N(h)=N(h− 1) +N(h− 2) + 1. 最少节点数n 如以斐波那契数列可以用数学归纳法证明: 即: N(0) = 0 ( ...

  7. 04-树4. Root of AVL Tree-平衡查找树AVL树的实现

    对于一棵普通的二叉查找树而言,在进行多次的插入或删除后,容易让树失去平衡,导致树的深度不是O(logN),而接近O(N),这样将大大减少对树的查找效率.一种解决办法就是要有一个称为平衡的附加的结构条件 ...

  8. AVL树和伸展树 -数据结构(C语言实现)

    读数据结构与算法分析 AVL树 带有平衡条件的二叉树,通常要求每颗树的左右子树深度差<=1 可以将破坏平衡的插入操作分为四种,最后通过旋转恢复平衡 破坏平衡的插入方式 描述 恢复平衡旋转方式 L ...

  9. 【数据结构】什么是AVL树

    目录 什么是AVL树 1. 什么是AVL树 2. 节点的实现 3. AVL树的调整 3.1 LL旋转 3.2 RR旋转 3.3 RL旋转 3.4 LR旋转 什么是AVL树 二叉查找树的一个局限性就是有 ...

随机推荐

  1. CF 2015 ICL, Finals, Div. 1 J. Ceizenpok’s formula [Lucas定理]

    http://codeforces.com/gym/100633/problem/J Lucas定理P不是质数裸题 #include <iostream> #include <cst ...

  2. typedef void(*Fun)(void);

    typedef void(*Fun)(void); 函数类似于数组,函数名就是它的首地址: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ...

  3. Filezilla Server 出现Error, could not connect to server解决办法

    打开任务管理器:Win+R:services.msc找到Filezilla Server并启动服务

  4. window 下生成NodeJs(v8.9.3) 的 VS2015 解决方案node.sln

    window 下生成NodeJs(v8.9.3) 的 VS2015 解决方案node.sln 使用步骤 也可以参照 github: https://github.com/nodejs/node/blo ...

  5. 使用单元素的枚举类型实现Singleton

    从java1.5版本开始,实现singleton出现了第三种方式: public enum SingleTon { INSTANCE; public void speak() { System.out ...

  6. java4 - 函数(方法)

    一.学习大纲: 1. 定义函数可以将功能封装 2. 函数的级别都是同级别的,不能进行函数套用 3. 便于对该功能进行复用 4. 函数只有被调用才能被执行 5. 函数的出现提高了代码的复用性 6. 函数 ...

  7. Windows系统上Redis的安装

    Redis 安装 Window 下安装 下载地址:https://github.com/MSOpenTech/redis/releases. Redis 支持 32 位和 64 位.这个需要根据你系统 ...

  8. 1.1 Python是一种什么样的语言

    小时不识月,呼作白玉盘.很多人习惯地说Python不过是一种脚本语言而已,实际上这种说法是非常不准确的,完全不能体现出Python的强大.严格来说,Python是一门跨平台.开源.免费的解释型高级动态 ...

  9. BloomFilter(布隆过滤器)

    原文链接:http://blog.csdn.net/qq_38646470/article/details/79431659 1.概念: 如果想判断一个元素是不是在一个集合里,一般想到的是将所有元素保 ...

  10. uva12563

    一个简单的0-1背包,背包容量为t-1,每个物品价值为1,代价为t[i].背包容量为t-1而不是t的原因是留1s唱<劲歌金曲>. AC代码: #include<cstdio> ...