二叉搜索树【数据结构】

二叉搜索树(Binary Search Tree)

(又:二叉搜索树,二叉排序树)

它或者是一棵空树,或者是具有下列性质的二叉树

  若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

  若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

  它的左、右子树也分别为二叉排序树 ————百度百科



二叉搜索树的基本操作:

构成:既然是二叉树那么构成一定包括了左子树、右子树、结点的值域

typedef struct BSTNode
{
	struct BSTNode* _left;		
	struct BSTNode* _right;		
	BSTDataType _data;
}BNode, *pBNode;

插入:在二叉搜索树中插入新元素时,必须先检测该元素是否在树中已经存在。如果已经存在,则不进行插入;否则将新元素加入到搜索停止的地方。 

1、树为空,则直接插入,插入节点为根节点

2、树不为空,则按照二叉搜索树的特性查找要插入的位置,插入

int InsertBSTree(pBNode* pRoot, BSTDataType data)
{
	pBNode pCur = NULL;			//插入的元素
	pBNode pParent = NULL;		//插入元素的双亲
	assert(pRoot);
	//空树
	if (NULL == *pRoot)		//空树则插入节点为根节点
	{
		*pRoot = BuyNode(data);
		return 1;
	}
	//找待插入元素的位置
	pCur = *pRoot;
	while (pCur)
	{
		if (data < pCur->_data)
		{
			pParent = pCur;
			pCur = pCur->_left;
		}
		else if (data > pCur->_data)
		{
			pParent = pCur;
			pCur = pCur->_right;
		}
		else
		{
			return 0;
		}
	}

	//插入新节点
	pCur = BuyNode(data);			//接收新的结点

	if (data > pParent->_data)
	{
		pParent->_right = pCur;
	}
	else
	{
		pParent->_left = pCur;
	}
	return 1;
}
删除:首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要  删除的结点可能分下面四种情况:  
a. 要删除的结点无孩子结点  
b. 要删除的结点只有左孩子结点  
c. 要删除的结点只有右孩子结点  
d. 要删除的结点有左、右孩子结点
 
情况a可以归类到c或者d  

对于上述情况,相应的删除方法如下:  
a. 直接删除该结点  
b. 删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子
结点  
c. 删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结
点  
d. 在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,在来处理该结点的删除问题 



int DeleteBSTree(pBNode* pRoot, BSTDataType data)
{
	pBNode pCur = NULL;					//标记当前结点
	pBNode pParent = NULL;				//标记当前结点的双亲节点
	pBNode pDel = NULL;					//标记要删除的树
	/* 存在? 空树? */
	assert(pRoot);
	if (NULL == *pRoot)
	{
		return 0;
	}

	/* 找出删除节点的位置 */
	pCur = *pRoot;
	while (pCur)
	{
		if (data == pCur->_data)
			break;
		else if (data < pCur->_data)
		{
			pParent = pCur;
			pCur = pCur->_left;
		}
		else
		{
			pParent = pCur;
			pCur = pCur->_right;
		}
	}
	if (NULL == pCur)//没找到要删除的结点
	{
		return 0;
	}

	/* 对节点进行删除  */
	//1.只有左孩子
	if (NULL == pCur->_right)
	{
		pDel = pCur;
		//1>节点是根节点------直接删
		if (pCur == *pRoot)
		{
			*pRoot = pCur->_left;
		}
		//2>节点是有左子树的结点-------让pParent 的左子树指向pCur的左子树
		else
		{
			if (pCur == pParent->_left)//删除没有2的1.
			{
				pParent->_left = pCur->_left;
			}
			else
			{
				pParent->_right = pCur->_left;
			}

		}
	}
	//2.只有右孩子
	else if (NULL == pCur->_left)
	{
		pDel = pCur;
		//1>根节点----直接删
		if (pCur == *pRoot)
		{
			*pRoot = pCur->_right;
		}
		//只有右子树的非根节点
		else
		{
			if (pCur == pParent->_right)
			{
				pParent->_right = pCur->_right;
			}
			else
			{
				pParent->_left = pCur->_right;
			}
		}
	}
	//3.左右孩子都有-----在右子树中找最小的节点替换后删除最小节点
	//              -----在左子树中找最大的结点替换后删除
	else
	{
		//方法一:在左子树中找最大的元素替换
		//pParent = pCur;
		//pDel = pParent->_left;
		//while (pDel->_right)
		//{
		//	pParent = pDel;
		//	pDel = pDel->_right;
		//}
		//pCur->_data = pDel->_data;
		//if (pDel == pParent->_right)  //要删除结点是双亲的右
		//{
		//	pParent->_right = pDel->_left;
		//}
		//else
		//{
		//	pParent->_left = pDel->_left;
		//}

		//方法二:在右子树中找最小的替换
		pParent = pCur;
		pDel = pCur->_right;

		while (pDel->_left)		//遍历找到最小的结点
		{
			pParent = pDel;
			pDel = pDel->_left;
		}
		pCur->_data = pDel->_data;		
		if ( pDel == pParent->_left)	//删除最小的结点
		{
			pParent->_left = pDel->_right;
		}
		else
		{
			pParent->_right = pDel->_right;
		}
	}

	free(pDel);
	pDel = NULL;
	return 1;
}
查找: 在子树中查找、找到返回true、否则返回False

pBNode FindBSTree(pBNode pRoot, BSTDataType data)
{
	pBNode pCur = pRoot;		//从根节点开始
	while (pCur)			//遍历整个二叉树
	{
		if (data == pCur->_data)
			return pCur;
		else if (data > pCur->_data)
		{
			pCur = pCur->_right;
		}
		else
		{
			pCur = pCur->_left;
		}
	}

	return NULL;
}

完整代码:

1.Test.c

#include "BSTree.h"
#include <stdlib.h>


int main()
{
	TestBSTree();
	system("pause");
	return 0;
}


2.BSTree.h

#pragma once				//二叉搜索树---O(N)  单支

typedef int BSTDataType;
#define NULL 0

typedef struct BSTNode
{
	struct BSTNode* _left;		
	struct BSTNode* _right;		
	BSTDataType _data;
}BNode, *pBNode;


void InitBSTree(pBNode* pRoot);						//初始化
int InsertBSTree(pBNode* pRoot, BSTDataType data);	//插入
int DeleteBSTree(pBNode* pRoot, BSTDataType data);	//删除
pBNode FindBSTree(pBNode pRoot, BSTDataType data);	//查找
void DestoryBSTree(pBNode* pRoot);					//销毁

int InsertBSTree_D(pBNode* pRoot, BSTDataType data);//递归插入
int DeleteBSTree_D(pBNode* pRoot, BSTDataType data);//递归删除
pBNode FindBSTree_D(pBNode pRoot, BSTDataType data);//递归查找


void InOrder(pBNode pRoot);							//中序遍历
pBNode BuyNode(BSTDataType data);					

void TestBSTree();


3.BSTree.c

#include "BSTree.h"
#include <malloc.h>
#include <assert.h>
#include <stdio.h>


void InitBSTree(pBNode* pRoot)
{
	assert(pRoot);
	*pRoot = NULL;
}
//构建新节点
pBNode BuyNode(BSTDataType data)
{
	pBNode pNode = (pBNode)malloc(sizeof(BNode));
	if (NULL == pNode)
	{
		return NULL;;
	}
	pNode->_data = data;
	pNode->_left = NULL;
	pNode->_right = NULL;
	return pNode;
}

int InsertBSTree_D(pBNode* pRoot, BSTDataType data)
{
	assert(pRoot);
	if (NULL == *pRoot)
	{
		*pRoot = BuyNode(data);
		return 1;
	}
	else
	{
		if (data == (*pRoot)->_data)
		{
			return 0;
		}
		if (data > (*pRoot)->_data)
		{
			return InsertBSTree_D(&(*pRoot)->_right, data);
		}
		else 
		{
			return InsertBSTree_D(&(*pRoot)->_left, data);
		}
	}
}

int InsertBSTree(pBNode* pRoot, BSTDataType data)
{
	pBNode pCur = NULL;			//插入的元素
	pBNode pParent = NULL;		//插入元素的双亲
	assert(pRoot);
	//空树
	if (NULL == *pRoot)		//空树则插入节点为根节点
	{
		*pRoot = BuyNode(data);
		return 1;
	}
	//找待插入元素的位置
	pCur = *pRoot;
	while (pCur)
	{
		if (data < pCur->_data)
		{
			pParent = pCur;
			pCur = pCur->_left;
		}
		else if (data > pCur->_data)
		{
			pParent = pCur;
			pCur = pCur->_right;
		}
		else
		{
			return 0;
		}
	}

	//插入新节点
	pCur = BuyNode(data);			//接收新的结点

	if (data > pParent->_data)
	{
		pParent->_right = pCur;
	}
	else
	{
		pParent->_left = pCur;
	}
	return 1;
}

int DeleteBSTree_D(pBNode* pRoot, BSTDataType data)
{
	assert(pRoot);
	if (NULL == *pRoot)
	{
		return 0;
	}
	else
	{
		if (data > (*pRoot)->_data)
		{
			return DeleteBSTree_D(&(*pRoot)->_right, data);
		}
		else if (data < (*pRoot)->_data)
		{
			return DeleteBSTree_D(&(*pRoot)->_left, data);
		}
		else
		{
			//已找到要删除的结点
			//1.只有左子树
			pBNode pDel = *pRoot;
				
			if (NULL == (*pRoot)->_right)
			{
				*pRoot = pDel->_left;
				free(pDel);
				return 1;
			}
			//2.只有右子树
			else if (NULL == (*pRoot)->_left)
			{
				*pRoot = pDel->_right;
				free(pDel);
				return 1;
			}
			//3.左右子树都存在
			else
			{
				//在右子树中找出要替换的结点(最小)
				//pBNode pParent = *pRoot;
				pDel = (*pRoot)->_right;
				while (pDel->_left)
				{
					//pParent = pDel;
					pDel = pDel->_left;
				}
				(*pRoot)->_data = pDel->_data;
				return DeleteBSTree_D(&(*pRoot)->_right, pDel->_data);
			}
		}
	}
}

int DeleteBSTree(pBNode* pRoot, BSTDataType data)
{
	pBNode pCur = NULL;					//标记当前结点
	pBNode pParent = NULL;				//标记当前结点的双亲节点
	pBNode pDel = NULL;					//标记要删除的树
	/* 存在? 空树? */
	assert(pRoot);
	if (NULL == *pRoot)
	{
		return 0;
	}

	/* 找出删除节点的位置 */
	pCur = *pRoot;
	while (pCur)
	{
		if (data == pCur->_data)
			break;
		else if (data < pCur->_data)
		{
			pParent = pCur;
			pCur = pCur->_left;
		}
		else
		{
			pParent = pCur;
			pCur = pCur->_right;
		}
	}
	if (NULL == pCur)//没找到要删除的结点
	{
		return 0;
	}

	/* 对节点进行删除  */
	//1.只有左孩子
	if (NULL == pCur->_right)
	{
		pDel = pCur;
		//1>节点是根节点------直接删
		if (pCur == *pRoot)
		{
			*pRoot = pCur->_left;
		}
		//2>节点是有左子树的结点-------让pParent 的左子树指向pCur的左子树
		else
		{
			if (pCur == pParent->_left)//删除没有2的1.
			{
				pParent->_left = pCur->_left;
			}
			else
			{
				pParent->_right = pCur->_left;
			}

		}
	}
	//2.只有右孩子
	else if (NULL == pCur->_left)
	{
		pDel = pCur;
		//1>根节点----直接删
		if (pCur == *pRoot)
		{
			*pRoot = pCur->_right;
		}
		//只有右子树的非根节点
		else
		{
			if (pCur == pParent->_right)
			{
				pParent->_right = pCur->_right;
			}
			else
			{
				pParent->_left = pCur->_right;
			}
		}
	}
	//3.左右孩子都有-----在右子树中找最小的节点替换后删除最小节点
	//              -----在左子树中找最大的结点替换后删除
	else
	{
		//方法一:在左子树中找最大的元素替换
		//pParent = pCur;
		//pDel = pParent->_left;
		//while (pDel->_right)
		//{
		//	pParent = pDel;
		//	pDel = pDel->_right;
		//}
		//pCur->_data = pDel->_data;
		//if (pDel == pParent->_right)  //要删除结点是双亲的右
		//{
		//	pParent->_right = pDel->_left;
		//}
		//else
		//{
		//	pParent->_left = pDel->_left;
		//}

		//方法二:在右子树中找最小的替换
		pParent = pCur;
		pDel = pCur->_right;

		while (pDel->_left)		//遍历找到最小的结点
		{
			pParent = pDel;
			pDel = pDel->_left;
		}
		pCur->_data = pDel->_data;		
		if ( pDel == pParent->_left)	//删除最小的结点
		{
			pParent->_left = pDel->_right;
		}
		else
		{
			pParent->_right = pDel->_right;
		}
	}

	free(pDel);
	pDel = NULL;
	return 1;
}


pBNode FindBSTree_D(pBNode pRoot, BSTDataType data)
{
	if (NULL == pRoot)
	{
		return NULL;
	}
	else
	{
		if (data == pRoot->_data)
		{
			return pRoot;
		}
		else if (data > pRoot->_data)
		{
			return FindBSTree_D(pRoot->_right, data);
		}
		else
		{
			return FindBSTree_D(pRoot->_left, data);
		}
	}
}

	


void InOrder(pBNode pRoot)
{
	if (NULL == pRoot)
	{
		return;
	}
	if (pRoot)
	{
		InOrder(pRoot->_left);
		printf("%d ", pRoot->_data);
		InOrder(pRoot->_right);
	}
}

void DestoryBSTree(pBNode* pRoot)		//递归销毁
{
	assert(pRoot);
	if (NULL == *pRoot)
	{
		return;
	}
	else
	{
		if (NULL != (*pRoot)->_left)		//左子树不为空
		{
			DestoryBSTree(&(*pRoot)->_left);
		}
		if (NULL != (*pRoot)->_right)		//右子树不为空
		{
			DestoryBSTree(&(*pRoot)->_right);
		}
		if (NULL == (*pRoot)->_right && NULL == (*pRoot)->_left)//叶子节点
		{
			free(*pRoot);
			*pRoot = NULL;
		}
	}

}

void TestBSTree()
{
	int array[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
	pBNode pRoot = NULL;
	pBNode pCur = NULL;
	int i = 0;
	int data = 10;

	InitBSTree(&pRoot);
	for (; i < sizeof(array) / sizeof(array[0]); i++)
	{
		InsertBSTree_D(&pRoot, array[i]);
	}
	InOrder(pRoot);
	printf("\n");
	DeleteBSTree(&pRoot, 7);
	InOrder(pRoot);
	printf("\n");

	pCur = FindBSTree(pRoot, data);
	if (pCur)
	{
		printf("%d is in BSTree!!!",data);
	}
	else
	{
		printf("%d is not in BSTree!!!", data);
	}
	DestoryBSTree(&pRoot);
	
}
二叉搜索树性能分析:

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。  
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多  
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树 .
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:Log
2N
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2



二叉搜索树(Binary Search Tree)是一种具有特定性质的二叉树。它可以是一棵空树,或者满足以下条件: 1. 如果左子树不为空,则左子树上所有节点的值都小于根节点的值。 2. 如果右子树不为空,则右子树上所有节点的值都大于根节点的值。 3. 左右子树也分别为二叉搜索树二叉搜索树作为一种经典的数据结构,具有许多优点。它既支持链表的快速插入和删除操作,又支持数组的快速查找操作。因此,在文件系统和数据库系统等领域,经常使用二叉搜索树来实现高效的排序和检索操作。 二叉搜索树的主要操作包括插入、遍历、查找和删除。插入操作将一个新节点插入到合适的位置,遍历操作按照规定的顺序访问树的各个节点,查找操作根据给定的值在树中找到对应的节点,删除操作将指定节点从树中删除。 关于二叉搜索树的性能分析,它的查找和插入操作的平均时间复杂度为O(logN),其中N是树中节点的数量。但是,如果树变得不平衡,最坏情况下的时间复杂度可能达到O(N)。为了避免树的不平衡,可以使用平衡二叉搜索树如AVL树进行优化。 总结来说,二叉搜索树是一种常用的数据结构,它具有快速插入和删除的特点,同时也支持高效的查找操作。但需要注意,为了保持性能,需要保持树的平衡。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [【数据结构二叉搜索树剖析(附源码)](https://blog.youkuaiyun.com/weixin_52344401/article/details/122908681)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [【Java 数据结构】实现一个二叉搜索树](https://blog.youkuaiyun.com/m0_61784621/article/details/128405350)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值