[查找] 2 二叉排序树 | 平衡二叉树 - C语言实现

二叉排序树

【二叉排序树】

  1. 二叉排序树可以是空树
  2. 结点p,它的左子树比它小,它的右子树比它大

【说明】

  1. 折半查找法的判定树就是一颗二叉排序树
  2. 对二叉排序树进行中序遍历,得到递增有序的序列

【基本操作】查找、插入、构建

typedef struct BTNode{
	int key;  //关键字
	struct BTNode *lchild;
	struct BTNOde *rchild;
}BiTNode, *BiTree;
// 查找的算法
BiTNode* BSTSearch(BiTree T, int key) {
	if (T) {
		if (T->key == key) return bt;
		else if ( key< T->key ) return BSTSearch(T->lchild, key);
		else return BSTSearch(T->rchild, key);
	}
}
// 插入
int BSTInsert(BiTree *pT, int key) {
	if ( (*pT)==NULL ) {
		*pT = (BiTNode *)malloc(sizeof(BiTNode)); if ( !*pT ) exit(0);
		(*pT)->key = key; (*pT)->lchild = (*pT)->rchild = NULL;
		return 1;
	} else {
		if ( (*pT)->key == key ) return 0;
		else if ( (*pT)->key > key ) return BSTInsert( &(*pT)->lchild, key);
		else return BSTInsert( &(*pT)->rchild, key);
	}
}
// 构建算法
void CreateBST(BiTree *pT, int key[], int n) {
	int i;
	*pT = NULL;
	for (i=0; i<n; i++) BSTInsert(pT, key[i]);
}

【删除】二叉排序树有约束条件,删除时注意,需要分类讨论(这里选择的是更好理解的方法)
假设删除结点p,f是p的爸爸

  1. 【情况1】p为叶子结点:直接删除p
  2. 【情况2】p只有一个孩子:将p删除,用p的孩子替换p
  3. 【情况3】p有两个孩子
    1. 【第一步】p的关键字要删除,找一个结点r把p的位置顶上,结点r有两种找法
      • 【第一种】去p的左子树上找:找p左子树上最大的结点(p往左走一步,再一直继续往右走,走到不能走,就找到了结点r)
      • 【第二种】去p的右子树上找:找p右子树上最小的结点(p往右走一步,再一直继续往左走,走到不能走,就找到了结点r)
    2. 【第二步】把r的关键字替换掉p的关键字(完成了p的删除):r->key = p->key
    3. 【第三步】删除r结点(删除r的时候,肯定属于第1、2种情况的一种)
// 删除关键字key
int DeleteBST(BiTree *pT, int key) {
	// 先查找到key,然后调用进行删除
	if ( *pT ) {
		if ( (*pT)->key == key ) return Delete(pT); //找到关键字
		else if ( (*pT)->key > key ) return DeleteBST( &(*pT)->lchild, key);
		else return DeleteBST( &(*pT)->rchild, key);
	}
}
// 删除操作
int Delete(BiTree *pT) {
	BiTNode *q, *r;
	if ( (*pT)->lchild==NULL && (*pT)->rchild==NULL ) { //情况一
		q = *pT;
		*pT = NULL;
		free(q);
	} else if ( (*pT)->lchild && (*pT)->rchild ) { //情况三
		r = (*pT)->lchild; //往左走一步
		wihle (r->rchild) { //再往右走到尽头
			r = r->rchild;
		}
		(*pT)->key = r->key; //r结点的key替换p,完成p关键字的删除
		return Delete( &r ); //删除r结点:一定是情况一、情况二,不会出现循环调用
	} else { //情况二
		q = *pT;
		*pT = (*pT)->lchild==NULL ? (*pT)->rchild : (*pT)->lchild; // 左子树为空 ? 挂上右子树 : 挂上左子树
		free(q);
	}
	return TRUE;
}

平衡二叉树

名称说明
二叉排序树有时候会很低效(ASL=(1+2+3+4+5)/5=3,和没有建树一样)《[查找] 2 二叉排序树 | 平衡二叉树 - C语言实现》
平衡二叉树为了不让它这么长,边的扁一点矮一点胖一点,改进出了平衡二叉树(ASL=(1+2+2+3+3)/5=2.2)《[查找] 2 二叉排序树 | 平衡二叉树 - C语言实现》

【约束条件】二叉排序树的进化,所有结点的左右子树高度之差的绝对值≤1
【平衡二叉树(AVL树)】一种特殊的二叉排序树,其左右子树都是平衡二叉树,且左右子树高度之差绝对值≤1
【定量判断】结点p,平衡因子=p左子树高度-p右子树高度,平衡因子的绝对值≤1
图中每个结点旁的数字代表该结点的平衡因子
《[查找] 2 二叉排序树 | 平衡二叉树 - C语言实现》

【平衡调整】|平衡因子|>1的结点即失去平衡,要进行平衡调整
【原理】

  1. 当失去平衡的最小子树被调整为平衡子树后,无需调整原有其他所有不平衡子树,整个二叉排序树就会变成一颗平衡二叉树
  2. 【失去平衡的最小子树】是以距离插入结点最近,且平衡因子绝对值大于1的结点作为根的子树

【步骤】

  1. 插入新结点
  2. 插入后,找出失去平衡的最小子树
  3. 调整这棵子树,使之成为平衡子树

【调整的四种情况】LL型、RR型、LR型、RL型

名字名字解释说明图例
LL(Left-Left)调整
右单旋转调整
在根(A)的左孩子(B)的左子树上插入结点,发生了不平衡1. A往下旋转一个位置,变成B的右孩子结点
2. B的右子树挂在A的左子树上
《[查找] 2 二叉排序树 | 平衡二叉树 - C语言实现》
RR(Right-Right)调整
左单旋转调整
在根(A)的右孩子(B)的右孩子上插入了结点,所发生的不平衡(是LL情况的对称情况)1. A往下旋转一个位置,变成B的左孩子
2. 将B的左孩子挂在A的右孩子上
《[查找] 2 二叉排序树 | 平衡二叉树 - C语言实现》
LR调整
先左后右双旋转调整
在根结点(A)左孩子(B)的右子树上插入结点,导致不平衡1. 把B整体摘下来,变成了RR的情况
2. 对B整体进行RR调整
3. 挂载A结点的空指针上,变成了LL的情况
4. 对A整体进行LL调整
《[查找] 2 二叉排序树 | 平衡二叉树 - C语言实现》
RL调整
先右后左双旋转调整
根结点(A)的右孩子的左子树上插入结点,导致的不平衡(是LR调整的对称情况)1. 把B整体摘下来,变成了LL的情况
2. 对B整体进行LL调整
3. 挂在A结点的空指针上,变成了RR的情况
4. 对A进行RR调整
《[查找] 2 二叉排序树 | 平衡二叉树 - C语言实现》
typedef struct BiTNode {
    int data; //关键字
    int bf;	//平衡因子
    struct BiTNode* lchild, *rchild;
}BiTNode, *BiTree;
#define LH +1 //左边高
#define EH 0 //左右高度相等
#define RH -1 //右边高
// 左单旋转调整(RR调整)
void L_Rotate(BiTree *A) {
    BiTree B = (*A)->rchild; //得到B
	B->bf = (*A)->bf = EH;
    (*A)->rchild = B->lchild; //B的左子树给A的右子树
    B->lchild = *A; //A挂到B的左子树上
    *A = B;
    return;
}
// 右单旋转调整(LL调整)
void R_Rotate(BiTree *A) {
    BiTree B = (*A)->lchild;
	B->bf = (*A)->bf = EH;
    (*A)->lchild = B->rchild;
    B->rchild = *A;
    *A = B;
    return;
}
// A的左子树变高了 - 有可能是LL、LR类型
void LeftBalance(BiTree* A) {
	BiTree B = (*A)->lchild; //A的左子树

	switch (B->bf) { //B的平衡因子
	case LH: //B的左边更高 --> LL型
		R_Rotate(A); //对A进行LL调整(即右单旋转调整)
		break;
	case RH: //B的右边高 --> LR型
		L_Rotate(&B);   //对B进行RR调整
		(*A)->lchild = B; //挂在A的左子树上,代替刚才的B
		R_Rotate(A);   //A变成了LL情况,对A进行LL调整
		break;
	}
}
// A的右子树变高了 - 有可能是RL、RR类型
void RightBalance(BiTree *A) {
	BiTree B = (*A)->rchild;
	
	switch (B->bf) { //B的平衡因子
	case LH: //B的左边更高 --> RL型
		R_Rotate(&B); //对B进行LL调整(即右单旋转调整)
		(*A)->rchild = B; //把新的根挂在A的空节点上
		L_Rotate(A); //对A进行RR调整(即左单旋转调整)
	case RH: //B的右边更高 --> RR型
		L_Rotate(A); //对A进行RR调整(即左单旋转调整)
		break;
	}
}
// 平衡二叉树 - 插入结点
int InsertAVL(BiTree* pT,int data,int *taller) {
	// @taller:该子树有没有变高,0未变高,1长高了

    if( *pT == NULL ) { //找到插入位置
        *pT = (BiTree)malloc(sizeof(BiTNode));   
        (*pT)->bf = EH; //左右高度相等
		(*pT)->rchild = (*pT)->lchild = NULL;
        (*pT)->data = data;
        *taller = 1; //这棵子树长高了
		return 1;
    } else {
		if ( data == (*pT)->data ) { //有相同的结点
			*taller = 0;
			return 0;
		} else if ( data < (*pT)->data ) {
			// 往左子树搜索
			if ( InsertAVL( &(*pT)->lchild, data, taller) ) {
				// 在左子树插入成功
				if (*taller) {
					// 左子树长高了
					switch ( (*pT)->bf ) { //没插入前pT的平衡因子是
					case LH: //没有插入前,pT左边就高出了1
						LeftBalance(pT); //插入后左边不平衡-->调整
						*taller = 0;	 //这棵没有变高,已经被处理掉了
						break;
					case EH: //没有插入前,pT两边平衡
						(*pT)->bf = LH; //插入后,左边高出了1
						*taller = 1;    //这棵子树变高了
						break;
					case RH: //没有插入前,pT右边高
						(*pT)->bf = EH; //插入后,左右平衡
						*taller = 0;	//这棵子树没有变高
						break;
					} //switch
				} // if
				return 1; //插入成功
			} //if 
		} else if ( data > (*pT)->data ) {
			// 往右子树搜索
			if ( InsertAVL( &(*pT)->rchild, data, taller) ) {
				// 在右子树插入成功
				if (*taller) {
					// 右子树长高了
					switch ( (*pT)->bf ) { //没插入前pT的平衡因子是
					case LH: //没插入前,pT的左子树高出了1
						(*pT)->bf = EH; //插入后,左右平衡
						*taller = 0;    //这棵子树没有变高
						break;
					case EH: //没插入前,pT两边平衡
						(*pT)->bf = RH; //插入后,右边高
						*taller = 1;    //这棵子树长高了
						break;
					case  RH: //没插入前,pT的右子树更高
						RightBalance(pT); //插入后,右子树不平衡
						*taller = 0;
						break;
					} //switch
				} //if
			} //if
			return 0;
		} // else if
	}
    return 0; //插入失败
}
    原文作者:二叉查找树
    原文地址: https://blog.csdn.net/summer_dew/article/details/84319196
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞