二叉查找树

==> 学习汇总(持续更新)
==> 从零搭建后端基础设施系列(一)-- 背景介绍


二叉查找树(英语:Binary Search Tree),也称二叉搜索树、有序二叉树(英语:ordered binary tree),排序二叉树(英语:sorted binary tree),是指一棵空树或者具有下列性质的二叉树:
1.若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2.若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3.任意节点的左、右子树也分别为二叉查找树;
4.没有键值相等的节点。

C代码下载
C++代码下载
java代码下载
(备用下载)

目录
1、查找
2、添加
3、删除
4、查找最小值
5、查找最大值
6、从小到大遍历
7、从大到小遍历

用如图二叉树进行说明






一、查找
步骤如下:
1.和根节点比较,若值相等,则查找成功(每一刻子树都有根节点)
2.若值小于根节点,则递归查找左子树
3.若值大于根节点,则递归查找右子树
4.重复1~3步骤,直到查找成功

如图:

C代码:

/*
* function				查找节点
* param   PBTreeSearch  二叉查找树指针
* param    int value    要查找的值
* return  PBTreeSearch  返回该结点的指针
*/
PBTreeSearch SearchNode(PBTreeSearch btree, int value)
{
	// 1.如果该节点为空,则退出
	if (!btree)
		return NULL;
	// 2.如果该节点的值等于value,则返回该节点
	else if (btree->value == value)
		return btree;
	// 3.如果该节点的值大于value,则递归遍历左子树
	else if (btree->value > value)
		SearchNode(btree->left, value);
	// 4.如果该节点的值小于value,则递归遍历右子树
	else if (btree->value < value)
		SearchNode(btree->right, value);
}

java代码:

	/*
	* function				查找节点
	* param	   Node			节点
	* param    int value    要查找的值
	* return   Node			成功返回该结点,否则返回null
	*/
	private Node searchNode(Node btree,int value) {
		// 1.如果该节点为空,则退出
		if (btree == null)
			return null;
		// 2.如果该节点的值等于value,则返回该节点
		else if (btree.compare(value) == 0)
			return btree;
		// 3.如果该节点的值大于value,则递归遍历左子树
		else if (btree.compare(value) == 1)
			return searchNode(btree.getLeft(), value);  //这里记得要加个return,否则最后会执行最后的return null
		// 4.如果该节点的值小于value,则递归遍历右子树
		else if (btree.compare(value) == -1)
			return searchNode(btree.getRight(), value);
		return null;
	}








二、添加
步骤如下:

1.若根节点为空,则直接创建根节点(第一次添加肯定走这一步,后面就不用走了)
2.先查找要添加的值存不存在,若存在,则该节点的count自加即可
3.若不存在,则创建一个新的节点node
a)如果该节点的值大于value,且其左节点为null,则将node作为该节点的左孩子
b)如果该节点的值小于value,且其右节点为null,则将node作为该节点的右孩子
c)如果该节点的值大于value,且其左节点不为null,则递归左子树
d)如果该节点的值小于value,且其右节点不为null,则递归右子树

如图:

C代码:

/*
* function				添加节点辅助函数
* param   PBTreeSearch  二叉查找树指针
* param   PBTreeSearch  要添加的节点
* return                无
*/
void Add(PBTreeSearch btree, PBTreeSearch node)
{
	if (!btree)
		btree = node;
	// 1.如果该节点的值大于value,且其左节点为null,则将node作为该节点的左孩子
	else if (btree->value > node->value && !btree->left)
		btree->left = node;
	// 2.如果该节点的值小于value,且其右节点为null,则将node作为该节点的右孩子
	else if (btree->value < node->value && !btree->right)
		btree->right = node;
	// 3.如果该节点的值大于value,且其左节点不为null,则递归左子树
	else if (btree->value > node->value && btree->left)
		Add(btree->left, node);
	// 4.如果该节点的值小于value,且其右节点不为null,则递归右子树
	else if (btree->value < node->value && btree->right)
		Add(btree->right, node);
}

/*
* function				添加节点
* param   PBTreeSearch  二叉查找树指针
* param    int value    要添加的值
* return                无
*/
void AddValue(PBTreeSearch* btree, int value)
{
	// 1.如果btree为null,说明当前是第一次添加根节点
	if (!(*btree))
		*btree = CreateNode(value);
	else
	{
		// 2.先查找value值,如果存在,则直接count++
		PBTreeSearch node = SearchNode(*btree, value);
		if (node)
			node->count++;
		else
		{
			// 3.如果该值不存在,则创建新结点
			node = CreateNode(value);

			// 4.添加节点
			Add(*btree, node);
		}
	}	
}








三、删除
删除要分为三种情况。
1.该节点无孩子,直接删除
2.该节点有且只有一个节点
3.该节点有两个孩子(这里有个技巧,那就是它的右子树中最小的值肯定比它的左孩子大,比它的右孩子小)

还需要注意的是删除前要找到其父节点,要将指向孩子的指针置空,否则遍历的时候会出错。下面的步骤中,已经将这三个步骤封装成一个函数了。所以直接说删除。

步骤如下:

1.如果二叉树为空,则删除失败
2.如果要删除的是根节点,则必须构造一个根节点的父节点,待删除完成后,再修改根节点的指向。(因为除了根节点之外,其它节点必定有父节点)
3.如果是其它结点
a)先查找该节点的父节点,若父节点不存在,则删除失败(说明删除的值不存在)
b)若父节点存在,则删除该节点,并修改父节点的指向。(这里包含了三个小步骤)


如图:





C代码:

/*
* function				删除节点辅助函数
* param   cur			需要删除的节点
* param   par			需要删除节点的父节点
* param   bLeft         cur是左节点还是右节点
* return                无
*/
void Delete(PBTreeSearch* cur, PBTreeSearch* par,bool bLeft)
{
	// 1.若该节点无孩子,则直接删除
	if (!(*cur)->left && !(*cur)->right)
	{
		free(*cur);
		if(bLeft)
			(*par)->left = NULL;
		else
			(*par)->right = NULL;
	}
	// 2.若该节点只有一个孩子,则将该孩子直接顶替到原来的位置上
	else if ((!(*cur)->left && (*cur)->right) || ((*cur)->left && !(*cur)->right))
	{
		PBTreeSearch temp = *cur;
		if (temp->left)  //根据bLeft的值来修改孩子节点
			if(bLeft)
				(*par)->left = temp->left;
			else
				(*par)->right = temp->left;
		else
			if(bLeft)
				(*par)->left = temp->right;
			else
				(*par)->right = temp->right;
		free(temp);
	}
	// 3.若该节点有两个孩子,则将该节点的右子树中最小的那个节点替换上去
	else
	{
		PBTreeSearch minNode = FindMinNode((*cur)->right);  //找到需要删除的节点的右子树中最小的节点
		PBTreeSearch parent = SearchParentNode((*cur), minNode->value); //同样,获取父节点是为了置孩子节点为null
		(*cur)->value = minNode->value; //修改值就好
		if (parent->left->value == minNode->value)  //如果是在左孩子中找到最小值
			parent->left = NULL;
		else if (parent->right->value == minNode->value) //如果是在右孩子中找到,则左孩子肯定为null,这时只需要修改指向即可
			(*cur)->right = minNode->right;
		free(minNode);
	}
}

/*
* function				删除节点
* param   PBTreeSearch  二叉查找树指针
* param    int value    要删除的值
* return                无
*/
void DeleteValue(PBTreeSearch* btree, int value)
{
	// 1.如果btree为空则退出
	if (!(*btree));
	// 2.如果该节点是根节点的话
	else if ((*btree)->value == value)
	{
		PBTreeSearch rootPar = (PBTreeSearch)malloc(sizeof(_btree_search));
		rootPar->left = *btree;		
		Delete(btree, &rootPar, true);   //这里模拟下面的,声明一个root的父节点,然后以同样的方式调用它
		*btree = rootPar->left;			 //最后要将root节点的指向修改	
		free(rootPar);
	}
	else
	{
		// 3.查找需要删除节点的父节点  (需要找到父节点是因为最后要将它的孩子置为null)
		PBTreeSearch par = SearchParentNode(*btree, value);
		if (!par)
			return;
		// 4.删除该节点(因为不懂是左还是右,所以要进一步判断)
		if (par->left && par->left->value == value)
			Delete(&par->left, &par, true);
		else
			Delete(&par->right, &par, false);
	}
}

java代码:

	/*
	* function				删除节点辅助函数
	* param   cur			需要删除的节点
	* param   par			需要删除节点的父节点
	* param   bLeft         cur是左节点还是右节点
	* return                无
	*/
	private void delete(Node cur, Node par, boolean bLeft) {
		// 1.若该节点无孩子,则直接删除
		if (cur.isLRNull()) {
			if (bLeft)
				par.setLeft(null);
			else
				par.setRight(null);
		}
		// 2.若该节点只有一个孩子,则将该孩子直接顶替到原来的位置上
		else if (cur.isOnlyOne()) {
			if (!cur.isLNull())  //根据bLeft的值来修改孩子节点
				if (bLeft)
					par.setLeft(cur.getLeft());
				else
					par.setRight(cur.getLeft());
			else
				if (bLeft)
					par.setLeft(cur.getRight());
				else
					par.setRight(cur.getRight());
		}
		// 3.若该节点有两个孩子,则将该节点的右子树中最小的那个节点替换上去
		else {
			Node minNode = findMinNode(cur.getRight());  //找到需要删除的节点的右子树中最小的节点
			Node parent = searchParentNode(cur, minNode.value); //同样,获取父节点是为了置孩子节点为null
			cur.value = minNode.value; //修改值就好
			if (parent.getLeft().compare(minNode.value) == 0)  //如果是在左孩子中找到最小值
				parent.setLeft(null);
			else if (parent.getRight().compare(minNode.value) == 0) //如果是在右孩子中找到,则左孩子肯定为null,这时只需要修改指向即可
				cur.setRight(minNode.getRight());
		}
	}
	
	/*
	* function				删除值
	* param    int value    要删除的值
	* return                删除失败返回false,否则返回true
	*/
	public boolean delete(int value) {
		// 1.如果btree为空则退出
		if (root == null) return false;
		// 2.如果该节点是根节点的话
		else if (root.compare(value) == 0) {
			Node rootPar = new Node(0);
			rootPar.setLeft(root);
			delete(root, rootPar, true);   		//这里模拟下面的,声明一个root的父节点,然后以同样的方式调用它
			root = rootPar.getLeft();			   	//最后要将root节点的指向修改	
			--nodeCount;		
		}
		else {
			// 3.查找需要删除节点的父节点  (需要找到父节点是因为最后要将它的孩子置为null)
			Node par = searchParentNode(root, value);
			if (par == null)
				return false;
			// 4.删除该节点(因为不懂是左还是右,所以要进一步判断)
			if (!par.isLNull() && par.getLeft().compare(value) == 0)
				delete(par.getLeft(), par, true);
			else
				delete(par.getRight(), par, false);
			--nodeCount;
		}
		return true;
	}








四、查找最小值
有一个规律就是,任意一个根节点开始,其最左边的节点必定是以该根节点构建的二叉树中值最小的。所以我们只需要迭代,找到其最左边的节点即可。

C代码:

/*
* function				查找最小节点
* param   PBTreeSearch  二叉查找树指针
* return                无
*/
PBTreeSearch FindMinNode(PBTreeSearch btree)
{
	if (!btree)
		return NULL;
	PBTreeSearch cur = btree;
	while (cur->left)
		cur = cur->left;
	return cur;
}
/*
* function				查找最小值
* param   PBTreeSearch  二叉查找树指针
* return                无
*/
int FindMinValue(PBTreeSearch btree)
{
	PBTreeSearch node = FindMinNode(btree);
	return node ? node->value : NO_FIND;
}








五、查找最大值

和查找最小值一样,其最右边的必定是最大值

/*
* function				查找最大值
* param   PBTreeSearch  二叉查找树指针
* return                无
*/
int FindMaxValue(PBTreeSearch btree)
{
	if (!btree)
		return NO_FIND;
	PBTreeSearch cur = btree;
	while (cur->right)
		cur = cur->right;
	return cur->value;
}








六、从小到大遍历

二叉查找树有这样一个规律,其 右孩子 > 根节点 > 左节点,所以遍历顺序为:左 -> 根 ->右

/*
* function				遍历二叉查找树(从小到大,用中序遍历即可)
* param   PBTreeSearch  二叉查找树指针
* return                无
*/
void PrintMinToMax(PBTreeSearch btree)
{
	if (btree)
	{
		PrintMinToMax(btree->left);
		int i = btree->count;
		while (i--)                     //循环输出相同的值
			printf("%d ", btree->value);
		PrintMinToMax(btree->right);
	}
}








七、从大到小遍历
二叉查找树有这样一个规律,其 右孩子 > 根节点 > 左节点,所以遍历顺序为:右 -> 根 ->左

/*
* function				遍历二叉查找树(从大到小,遍历顺序:右 -> 根 -> 左)
* param   PBTreeSearch  二叉查找树指针
* return                无
*/
void PrintMaxToMin(PBTreeSearch btree)
{
	if (btree)
	{
		PrintMaxToMin(btree->right);
		int i = btree->count;
		while (i--)                       //循环输出相同的值
			printf("%d ", btree->value);
		PrintMaxToMin(btree->left);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值