二叉搜索树的插入删除查找操作(集大成版)

第一部分 二叉搜索树

1、二叉树的相关定义

二叉搜索树即二叉排序树,又叫二叉查找树

定义:
二叉排序树或者是一颗空树,或者是具有下列性质的二叉树:
(1)若它的左子树不空,则左子树上的所有结点的值均小于它的根结点的值。
(2)若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值。
(3)它的左、右子树叶分别是二叉排序树。

值得注意的是:当用线性表作为表的组织形式时,可以有三种查找法。其中以二分查找效率最高。但由于二分查找要求表中结点按关键字有序,且不能用链表作存储结构,因此,当表的插入或删除操作频繁时,为维护表的有序性,势必要频繁移动表中结点。这种由移动结点引起的额外时间开销,就会抵消二分查找的优点。也就是说,二分查找只适用于静态查找表。若要对动态查找表进行高效率的查找,可采用下二叉搜索树

特点是:按照中序遍历该树所得到的中序序列是一个递增有序序列。
在这里插入图片描述
上面的二叉搜索树中序序列为2 3 4 5 7 8

2二叉搜索树的操作
树节点的定义

typedef class _BiTree{
public: 
	int data;
    _BiTree* left;
    _BiTree* right;
    _BiTree(int data){
        this->data=data;
    }
}BiTree;

二叉搜索树查找关键字
在二叉排序树不为空树的前提下,首先用关键字和树的根结点进行比较,会有 3 种不同的结果:
如果相等,查找成功;
如果比较结果为根结点的关键字值较大,则说明该关键字可能存在其左子树中;
如果比较结果为根结点的关键字值较小,则说明该关键字可能存在其右子树中;

BiTree* SearchBST(BiTree* T,KeyType key){
    //如果递归过程中 T 为空,则查找结果,返回NULL;或者查找成功,返回指向该关键字的指针
    if (!T || key==T->data) {
        return T;
    }else if(key<T->data){
        //递归遍历其左孩子
        return SearchBST(T->lchild, key);
    }else{
        //递归遍历其右孩子
        return SearchBST(T->rchild, key);
    }
}

二叉搜索树中插入关键字
二叉排序树本身是动态查找表的一种表示形式,有时会在查找过程中插入或者删除表中元素,当因为查找失败而需要插入数据元素时,该数据元素的插入位置一定位于二叉排序树的叶子结点,并且一定是查找失败时访问的最后一个结点的左孩子或者右孩子。可见插入过程和查找过程类似

bool InsertBST(BiTree** T, int key) {
	if (!(*T)) { 
		BiTree* newnode = new BiTree(key);
		*T = newnode;
		return true;
	}
	else if ((*T)->data == key) return false;
	else if ((*T)->data > key) {
		return InsertBST(&((*T)->left), key);
	}
	else if ((*T)->data < key) {
		return InsertBST(&((*T)->right), key);
	}
}

二叉搜索树的删除
删除可为BST问题最为复杂的一部分,需要考虑一下要删除的节点的四种情况:
该节点为叶子节点,删除即可
该节点只有左子树,没有右子树,删除后将该节点的左子树连接到该节点的父节点即可
该节点只有右子树,没有左子树,删除后将该节点的右子树连接到该节点的父节点即可
该节点既有左子树,也有右子树,这时候删除比较复杂。
首先,我们知道二叉排序树经过中序遍历后得到的是一个递增有序序列,该节点的前一个即为直接前驱,后一个为直接后继。我们要得到直接前驱的节点。
设要删除节点为p, p的双亲节点为f, p的左子树为left, 右子树为right。
方法一:left接到f上,将right接到p的直接前驱的右子树上,为何接到直接前驱的右子树上,因为直接前驱已经是左子树的最大值了。
在这里插入图片描述
直接前驱是p节点左子树的最右边的孩子,则直接前驱有可能有左子树,但不可能有右子树。
则方法二:用结点 p 的直接前驱(或直接后继)来代替结点 p,同时在二叉排序树中对其直接前驱(或直接后继)做删除操作。如果直接前驱有左子树,则将左子树接到直接前驱的双亲节点上。
在这里插入图片描述
现在就方法二,写出实现代码。

bool mydelete(BiTree** nodepp) {
	BiTree* nodep = *nodepp;
	BiTree* tmp, * q, * s;
	if (!nodep->right && !nodep->left) {
		*nodepp = NULL;
		return true;
	}
	else if (nodep->left == NULL || nodep->right == NULL) {
		*nodepp = nodep->left ? nodep->left : nodep->right;
		free(nodep);
		return true;
	}
	else {
		q = nodep;
		s = nodep->left;
		while (s->right) {
			q = s;
			s = s->right;
		}
		nodep->data = s->data;
		//判断q是否没有移动
		if (q == nodep) q->left = s->left; //无需判断s->left是否为空
		else q->right = s->left; 
		free(s);
		return true;
	}
}

bool DeleteBST(BiTree** T,int k) {
	if (*T == NULL)
		return false;
	if ((*T)->data == k) {
		return mydelete(T);
	}
	else if ((*T)->data > k) {
		return DeleteBST(&((*T)->left), k);
	}
	else if ((*T)->data < k) {
		return DeleteBST(&((*T)->right), k);
	}
}

总结
二叉搜索树的查找节点的时间复杂度,和节点所在的深度有关。
为了弥补二叉排序树构造时产生如下图所示的影响算法效率的因素,需要对二叉排序树做“平衡化”处理,使其成为一棵平衡二叉树。
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值