二叉树数组
什么是二叉树?数据结构
父节点至多只有两个子树的树形结构成为二叉树。以下图所示,图1不是二叉树,图2是一棵二叉树。ide
图1 普通的树 图2 二叉树spa
若是一棵树全部的非叶子节点都有两个子节点,则称该树为彻底二叉树,图2就是一棵彻底二叉树。3d
二叉查找树(ADT)指针
二叉树一个重要的应用是二差查找树,顾名思义,二叉查找树是二叉树在查找方面的应用。根据以往的知识,若是给你一个数组或者是链表,可能须要遍历一整个数组或者是量表才能找到须要查找的目标。使用二叉树做为查找的数据结构,可以大大缩小查找的深度(若链表和数组的长度为N,一棵二叉树的深度为logN),提升查找的效率。code
如何定义一棵查找树?blog
设每个节点对应一个键值,而该节点的左子节点对用的数值小于该节点的值,该节点的右子节点的数值大于该节点的值。以下图所示:it
图3 二差查找树 图4 非二差查找树event
图3是二叉查找树,由于他总数知足上述的条件,而图4不是二叉查找树,图中红色框住的部分不知足二叉查找树的条件(父节点6<左子节点7)。
代码:
struct Tree{
double value;
Tree *left;
Tree *right;
};
TreeNode表示二叉树节点。其中value表示树对应的值,left表示该节点的左节点指针,right表示该节点的右节点指针。
二叉查找树的重要操做
1.查找
1.1 查找固定的数值
给定一个数值,查找二叉树中是否有对应的数值,若是树中包含该数值,则返回数值对应的节点,不然,返回NULL。查找的过程:
首先定位到跟节点,若是查找的数值跟节点的数值相等,则根节点为所求;不然,若是数值小于根节点的数值,则查找根节点的左节点,相反,则查找跟节点的右节点。这样一直遍历下去,知道找到对应的或者或者已经不能继续往下查找为止(即已经到达子节点)。
代码:
1 Tree* Find(double value,Tree *t){ 2 if(t==NULL) 3 return NULL; 4 if(value==t->value) 5 return t; 6 else if(value<t->value) 7 return Find(value,t->left); 8 else 9 return Find(value,t->right); 10 }
1.2查找最大值或最小值
二叉查找树一个很大的特色是左子节点的数值<父节点的数值,而右子节点的数值>父节点的数值,所以查找最大值或者最小值就至关的方便,只须要从跟节点开始,不断遍历左子结点,直到到达叶子节点,就能够获得最小值;而查找最大值则从根节点开始遍历右子结点,直到到达叶子节点,对用的数值即为最大值。
代码:
超找最小元素的节点
1 Tree* FindMin(Tree* t){ 2 if(t==NULL) 3 return NULL; 4 if(t->left==NULL) 5 return t; 6 else{ 7 return FindMin(t->left); 8 } 9 }
查找最大元素的节点
1 Tree* FindMax(Tree *t){ 2 if(t==NULL) 3 return NULL; 4 if(t->right==NULL) 5 return t->right; 6 else 7 return FindMax(t->right); 8 }
2. 插入
插入操做是将一个数值插入到二叉查找树中的过程,插入后的树依然知足二叉查找树的条件。一棵二叉查找树的构建过程,就是一个不断将元素插入到二叉树的过程。
插入的操做最关键的是一个查找的过程,若是在二叉树中找到要插入的数值,则什么都不用作,若是找不到,只须要在最后咱们查找的叶子节点上新增长一个子节点便可。举一个例子:
上图是一棵二叉树,若是咱们要插入11,则按如下步骤进行:
再好比要找17,按如下步骤查找:
代码:
1 void Insert(double value,Tree* t){ 2 if(t==NULL){ 3 t = new Tree(); 4 if(t==NULL) 5 return; 6 else{ 7 t->value=value; 8 t->left=NULL; 9 t->right=NULL; 10 } 11 } 12 if(t->value>value) 13 Insert(value,t->left); 14 else 15 Insert(value,t->right); 16 }
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和四、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树的条件,须要先进性“左旋转”,再进行“右旋转”便可。
代码:
1 AVLTree* AVLInsert(double value,AVLTree * tree){ 2 if(tree==NULL){ 3 tree = new AVLTree(); 4 tree->value=value; 5 tree->left=NULL; 6 tree->right=NULL; 7 tree->height=0; 8 return; 9 } 10 if(value>tree->value){ 11 tree->right=AVLInsert(value,tree->right); 12 if(Height(tree->right)-Height(tree->left)==2){ 13 if(value>tree->right->value) 14 tree=SingleRotateWithRight(tree); 15 else 16 tree=DoubleRouteWithRight(tree); 17 } 18 } 19 else{ 20 tree->left=AVLInsert(value,tree->left); 21 if(Height(tree->left)-Height(tree->right)==2){ 22 if(value<tree->left->value) 23 tree=SingleRotateWithLeft(tree); 24 else 25 tree=DoubleRouteWithLeft(tree); 26 } 27 } 28 tree->height = Height(tree->left)>Height(tree->right)?Height(tree->left)+1:Height(tree->right)+1; 29 return tree; 30 } 31 //左旋转 32 AVLTree* SingleRotateWithLeft(AVLTree * tree){ 33 AVLTree * left = tree->left; 34 tree->left=left->right; 35 left->right=tree; 36 tree->height=Height(tree->left)>Height(tree->right)?Height(tree->left)+1:Height(tree->right)+1; 37 left->height=Height(left->left)>Height(left->right)?Height(left->left)+1:Height(left->right)+1; 38 return left; 39 } 40 //右旋转 41 AVLTree* SingleRotateWithRight(AVLTree * tree){ 42 AVLTree * right = tree->right; 43 tree->right=right->left; 44 right->left=tree; 45 tree->height=Height(tree->left)>Height(tree->right)?Height(tree->left)+1:Height(tree->right)+1; 46 right->height=Height(right->left)>Height(right->right)?Height(right->left)+1:Height(right->right)+1; 47 return right; 48 } 49 //右—左双旋转 50 AVLTree* DoubleRouteWithLeft(AVLTree* tree){ 51 tree->left = SingleRotateWithRight(tree->left); 52 return SingleRotateWithLeft(tree); 53 } 54 //右—左双旋转 55 AVLTree* DoubleRouteWithRight(AVLTree* tree){ 56 tree->right = SingleRotateWithLeft(tree->right); 57 return SingleRotateWithRight(tree); 58 }