文章目录
如果数据集是普通的顺序存储,即整个数据表无序存储。那么插入操作就是放在表的末端,表元素个数加1即可。删除操作可以是删除后,后面的元素前移。或者是待删除元素与末尾元素互换,同时表元素个数减一。但是表无序造成的查找效率很低。
如果查找的数据表是有序线性表,并且是顺序存储的,查找可以用折半,插值等算法实现,但是因为数据表有序,导致插入和删除操作,需要消耗大量的时间。
那么我们希望找到一种数据结构,可以使得插入和删除效率不错,同时可以满足高效率的查找操作。
二叉排序树就可以满足这个要求,二叉排序树又称为二叉查找树,它或者是一棵空树,或者满足下列性质:
1.若它的左子树不空,则左子树所有节点的值均小于根节点的值。
2.若它的右子树不空,则右子树所有节点的值均大于根节点的值。
3.它的左右子树也分别是二叉排序树。
二叉排序树非线性的结构,有利于插入和删除的实现。当对它进行中序遍历时,就可以得到有序的序列。
为了详细说明二叉树,我们先提供一个二叉树的结构。
1 2 3 4 5
| typedef struct BiTNode { int data; struct BiTNode *lchild,*rchild; }BiTNode,*BiTree;
|
这种用法的意思是给struct BitNode取别名位BitNode,给struct BitNode *取别名为BitTree,所以BitTree声明的变量是一个指针。
下面来说明下二叉排序树的查找的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Status SearchBst(BiTree T,int key,BiTree f,BiTree *p){ if(!T){ *p = f; return FALSE; } else if(key = T->data) { *p = T; return TRUE; } else if(key<T->data) return SearchBST(T->lchild,key,T,p); else return SearchBST(T->rchild,key,T,p); }
|
SearchBST函数是一个可递归运行的函数,key代表要查找的关键字,二叉树f指向T的双亲,当T指向根节点时,f的初值就为NULL,它在递归时有用,最后的参数p是为了查找成功后可以得到查找到的结点位置。
二叉排序树的插入操作需要借助查找操作,插入就是将关键字放到树中的合适位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Status InsertBST(BiTree *T ,int key) { BiTree p,s; if(!SearchBST(*T,key,NULL,&p)) { s = (BiTree)malloc(sizeof(BiTnode)); s->data = key; s->lchild = s->rchild = NULL; if(!p) { *T = s; } else if(key<p->data){ p->lchild = s; } else p->rchild = s; return TRUE; } else return FALSE; }
|
二叉树的构建就是利用循环逐步插入节点生成二叉排序树。
二叉树对于插入和查找都很方便,但是为了满足二叉树节点删除后,仍满足二叉树的特性,因此删除节点需要考虑多种情况。
1.删除叶子节点。删除叶子节点对整棵树的其它节点结构没有影响,直接删除即可。
2.要删除的节点只有左子树或者右子树的情况,将节点删除后,将它的左子树或者右子树整个移动到删除节点的位置。
3.对于要删除的节点,既有左子树也有右子树的情况,可以找一个节点代替被删除节点的位置。当该节点是与被删除节点大小最相近的节点时,整个树结构不改变。即该节点可以是待删除节点左子树的最右节点,或者是右子树的最左节点。也同样是中序遍历中与删除节点相邻的节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| Status DeleteBST(BiTree *T ,int key) { if(!*T) return FALSE; else { if(key == (*T)->data) return Delete(T); else if(key < (*T)->data) return DeleteBST(& (*T)->lchild,key); else return DeleteBST(&(*T)->rchild,key); } } status Delete(BiTree *p) { BiTree q,s; if((*p)->rchild == NULL) { q = *p; *p = (*p)->lchild; free(q); } else if((*p)->lchild == NULL) { q = *p; *p = (*p)->rchild; free(q); } else { q = *p; s = (*p) ->lchild; while(s->rchild) { q =s; s = s->rchild; } (*p)->data = s->data; if(q!=*p) q->rchild = s->lchild; else q->lchild = s->lchild; } }
|
二叉排序树以链接形式存储,在插入和删除过程中,不需要移动元素,只要修改链接指针即可。对于查找来说,走的就是从根节点到要查找的节点的路径,比较次数等于节点在树中的层数。因此降低二叉树的深度有利于查找节点。也就是将二叉树构建成平衡二叉树。
平衡二叉树是二叉排序树的一种,其中每个节点的左子树和右子树的高度差至多等于1.空树也是平衡二叉树的一种。二叉树节点的左子树深度减去右子树深度的值称为平衡因子bf。若bf>1,则二叉树不平衡。
距离插入节点最近的,且平衡因子的绝对值大于1的节点为根的子树,为最小不平衡子树。平衡二叉树构建的基本思想就是在构建二叉排序树的过程中,每插入一个节点时,先检查是否因为插入节点而破坏了树的平衡性,若是,则找出最小不平衡子树,在保持二叉排序树特性的前提下,调整最小不平衡子树各节点的链接关系,进行相应的旋转,使之成为新的平衡子树。
平衡二叉树相对于二叉排序树,需要增加bf,用于存储平衡因子。
1 2 3 4 5 6
| typedef struct BiTNode { int data; struct BiTNode *lchild,*rchild; int bf; }BiTNode,*BiTree;
|
平衡二叉树,就是在二叉排序树的创建过程中保持它的平衡性,一旦有不平衡的情况,马上处理,当一个节点的bf为正时,进行右旋操作,为负时,进行左旋操作。当该节点与其子节点bf值符号不一致时,先处理子节点。
右旋操作代码如下,其中p是指要右旋处理的树的根节点:
1 2 3 4 5 6 7 8
| void R_Rotate(BiTree *p) { BiTree L; L = (*p) ->lchild; (*p)->lchild = L->rchild; L->rchild = (*p); *p = L; }
|
左旋操作和右旋操作对称,代码如下:
1 2 3 4 5 6 7 8
| void L_Rotata(BiTree *p) { BiTree R; R = (*p)->rchild; (*p) ->rchile = R->lchild; R->lchild = (*p); *p = R; }
|
下面是左平衡旋转处理的代码,也就是当T节点已经不平衡的时候,并且是左边的高于右边的进行处理的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #define LH +1 #define EH 0 #define RH -1 void LeftBalance(BiTree *T) { BiTree L,Lr; L = (*T)->lchild; switch(L->bf) { case LH: (*T)->bf = L->bf = EH; R_Rotate(T); break; case RH: Lr = L->rchild; switch(Lr->bf) { case LH: (*T)->bf = RH; L->bf = EH; break; case EH: (*T)->br = L->br = EH; break; case RH: (*T)->bf = EH; L->bf = LH; break; } Lr->bf = EH; L->Rotate(&(*T)->lchild); R_Rotate(T); } }
|
右平衡处理类似于左平衡处理。
下面是主函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| Status InsertAVL(BiTree *T,int e,Status *taller) { if(!*T) { *T = (BiTree)malloc(sizeof(BiTNode)); (*T)->data = e; (*T)->lchild = (*T)->rchild = NULL; (*T)->bf=EH; *taller = TRUE; } else { if(e==(*T)->data) { *taller = FALSE; return FALSE; } if(e<(*T)->data) { if(!InsertAVL(&(*T)->lchild,e,taller)) return FALSE; if(*taller) { switch((*T)->bf) { case LH: LeftBalance(T); *taller = FALSE; break; case EH: (*T)->bf=LH; *taller = TRUE; break; case RH: (*T)->bf = EH; *taller = FALSE; break; } } } else { if(!InsertAVL(&(*T)->rchild,e,taller)) return FALSE; if(*taller) { switch((*T)->bf) { case LH: (*T)->bf = EH; *taller = FALSE; break; case EH: (*T)->bf = RH; *taller = TRUE; break; case RH: RightBalance(T); *taller = FASLE; break; } } } } return TRUE; }
|
不平衡的二叉树查找效率较低,而平衡二叉树插入删除和查找的时间复杂度O(logn)。