二叉搜索树

一、二叉搜索树的查找和插入:

#pragma once
# include<stdio.h>
# include<stdlib.h>
# include<assert.h>
# include<malloc.h>


typedef int DataType;
typedef struct BSTreeNode
{
	struct BSTreeNode* _pLeft;
	struct BSTreeNode* _pRight;
	DataType _data;
}BSTreeNode;

void InitBST(BSTreeNode** pRoot)
{
	*pRoot = NULL;
}

BSTreeNode* BuyBSTreeNode(DataType data)
{
	BSTreeNode* pNewNode = (BSTreeNode*)malloc(sizeof(BSTreeNode));
	if (NULL == pNewNode)
	{
		assert(0);
		return NULL;
	}
	pNewNode->_data = data;
	pNewNode->_pLeft = NULL;
	pNewNode->_pRight = NULL;
	return pNewNode;
}
void InsertBSTree(BSTreeNode** pRoot,DataType data)
{
	BSTreeNode* pCur=NULL;
	BSTreeNode* pParent = NULL;
	assert(pRoot);
	if (NULL == *pRoot)
	{
		*pRoot = BuyBSTreeNode(data);
		return;
	}
	//树不为空
	//查找待插入节点的位置(从根的根的位置往下找
	pCur = *pRoot;
	while (pCur){
		if (data < pCur->_data)
		{
			//先将双亲节点保存下来
			pParent = pCur;
			pCur = pCur->_pLeft;
		}
		else if (data>pCur->_data)
		{
			//先将双亲节点保存下来,否则不知道插入节点时将要插入到哪里
			pParent = pCur;
			pCur = pCur->_pRight;
		}
		else//数据存在则不需要发送
			return;
	}
	//插入新节点
	//新节点所在的位置
	pCur = BuyBSTreeNode(data);
	if (data < pParent->_data)
		pParent->_pLeft = pCur;
	else
		pParent->_pRight = pCur;
}

int FindBSTree(BSTreeNode* pRoot, DataType data)
{
	BSTreeNode* pCur = pRoot;
	while (pCur){
		if (data == pCur->_data)
			return 1;//要查找的节点在树里面
		else if (data < pCur->_data)
			pCur = pCur->_pLeft;
		else
			pCur = pCur->_pRight;
	}
	return 0;
}

void PreOrder(BSTreeNode* pRoot)
{
	if (pRoot){
		PreOrder(pRoot->_pLeft);
		printf("%d ", pRoot->_data);
		PreOrder(pRoot->_pRight);
	}
}

void DestroyBSTree(BSTreeNode** pRoot)
{
	assert(pRoot);
	if (*pRoot){
		DestroyBSTree(&(*pRoot)->_pLeft);
		DestroyBSTree(&(*pRoot)->_pRight);
		free(*pRoot);
		*pRoot = NULL;
	}
}
void TestBSTree()
{
	int a[] = { 1, 5, 3, 71, 8, 9, 42, 36 };
	BSTreeNode* pRoot;
	int i = 0;
	InitBST(&pRoot);
	for (; i < sizeof(a) / sizeof(a[0]);++i)
	{
		InsertBSTree(&pRoot, a[i]);
	}
	printf("中序遍历:");
	PreOrder(pRoot);
	printf("\n");
	DestroyBSTree(&pRoot);
}

删除时:1、找值为x的节点

2、删除:

(1)没有左右孩子(叶子节点):可以直接删除,删除后将叶子节点的上一级节点指向空(否则会成为野指针)。

(2)只有左孩子:

a、待删除节点不是根节点:带删除节点是双亲的左孩子:删除左子树的左孩子(该左孩子没有右孩子):让双亲的左孩子指向要删除的左孩子

b、待删除节点不是根节点:待删除节点是双亲的右孩子:删除右子树的左孩子:让双亲的右指向待删除元素的左

c、待删除节点是根节点:直接删除:根节点只有左子树,没有右子树:不改变根的指向

(3)只有右孩子:

(4)左右孩子均存在:在右子树中找最小的节点,交给待删除节点,在右子树中删除替代节点

void DeleteBSTree(BSTreeNode** pRoot,DataType data)
{
	BSTreeNode* pCur = NULL;
	BSTreeNode* pPrent = NULL;
	assert(pRoot);//指向头节点的地址,一定不会为空
	//找待删除节点的位置
	pCur = *pRoot;//从根的位置开始找
	while (pCur)
	{
		//查看要删除的节点是不是要删除的数据
		if (data == pCur->_data)
			break;//找到了则退出
		else if (data < pCur->_data)
		{
			pPrent = pCur;//把双亲保存出来
			pCur = pCur->_pLeft;
		}
		else
 		{
			pPrent = pCur;
			pCur = pCur->_pRight;
		}
	}
	//待删除元素不在树中
	if (NULL==pCur)
	{
		return;
	}
	//找到该节点
	if (NULL==pCur->_pLeft)
		//左孩子为空,只有右子树,要么是叶子节点,要么是右子树
	{
		//待删除节点是根节点
		if (pCur == *pRoot)
			*pRoot = pCur->_pRight;
		else
		{
			//待删除节点是其双亲的左孩子
			if (pCur == pPrent->_pLeft)
				pPrent->_pLeft = pCur->_pRight;
			else
				pPrent->_pRight = pCur->_pRight;
		}
	}
	else if (NULL == pCur->_pRight)//只有左子树
	{
		//待删除节点为右子树
		if (pRoot == pCur)
			//直接删除pRoot
		{
			*pRoot = pCur->_pLeft;
		}
		else
		{
			//待删除节点是其双亲的左孩子
			if (pCur == pPrent->_pLeft)
				pPrent->_pLeft = pCur->_pLeft;
			else
				pPrent->_pRight = pCur->_pLeft;
		}
	}
	else
	{
		//待删除节点左右孩子都存在
		//在其右子树中找最小(最左边)的节点或者
		//在右子树中找最大(最右侧)的节点
		BSTreeNode* pDel = pCur->_pRight;//在右子树中查找
                pParent=pCur;//不加这句话,会导致pParent不进行更新
		while (pDel->_pLeft){//左子树存在
			pPrent = pDel;
			pDel = pDel->_pLeft;
		}
		//找到了最左边的节点
		pCur->_data = pDel->_data;
		//删除pDel
		if (pDel == pPrent->_pLeft)
			pPrent->_pLeft = pDel->_pRight;
		else
			pPrent->_pRight = pDel->_pRight;
		pCur=pDel;
	}
	free(pCur);
}

二、递归实现二叉树的增加,查找,删除

int FindBSTree(BSTreeNode* pRoot,DataType data)
{
	if (NULL == pRoot)
		return;
	if (data == pRoot->_data)
		return 1;
	else if (data < pRoot->_data)
		return FindBSTree(pRoot->_pLeft, data);
	else
		return FindBSTree(pRoot->_pRight, data);
}
void InserBSTree(BSTreeNode** pRoot,DataType data)
{
	if (NULL == *pRoot)
		*pRoot = BuyBSTreeNode(data);
	else{
		if (data < (*pRoot)->_data)//在左子树里
			return InserBSTree(&(*pRoot)->_pLeft, data);
		else if (data>(*pRoot)->_data)//在右子树里
			return InserBSTree(&(*pRoot)->_pRight, data);
		else//相等,元素已经存在
			return 0;
	}
}
void DeleteBSTree(BSTreeNode** pRoot, DataType data)
{
	if (NULL == *pRoot)
		return;
	if (data < (*pRoot)->_data)
		return DeleteBSTree(&(*pRoot)->_pLeft, data);
	else if (data>(*pRoot)->_data)
		return DeleteBSTree(&(*pRoot)->_pRight, data);
	else
	{
		BSTreeNode* pDel = *pRoot;
		if (NULL == pDel->_pLeft)
		{
			*pRoot = pDel->_pRight;
			free(pDel);
		}
		else if (NULL == pDel->_pRight)
		{
			*pRoot = pDel->_pLeft;
			free(pDel);
		}
		else{//左右子树都存在,到右子树里面找节点
			BSTreeNode* pDel = (*pRoot)->_pRight;
			while (pDel->_pLeft)
				pDel = pDel->_pLeft;
			//替代节点
			(*pRoot)->_data = pDel->_data;
			return DeleteBSTree(&(*pRoot)->_pRight, pDel->_data);
		}
	}
}

二叉树的插入先找位置(若树不为空,则从根开始插入,二叉搜索树中插入的每一个值都是唯一的。比根节点小则朝左边插入,比根节点大则向右侧插入,在这个过程中需要把双亲节点保存起来,pCur走到空的位置,位置找到了保存在双亲的指针域中),再插入。

二叉树的删除:也分为两步:(1)找节点(先和根相比,如果和根相等则找到了;如果不相等,比根小则朝左边找,若是比根大则朝右边找,只要节点找到了就可有将节点删除(2)删除:分为四种情况:a、叶子节点;b、只有右孩子;c、只有左孩子;

d、左右孩子都存在【叶子节点可以和只有左孩子或者只有右孩子合并起来】

三、二叉搜索树的应用

1、判断一个单词是否拼写正确

(1)方式一:把单词和集合里面的单词一个个进行对比,如果单词和集合里面的某一个单词相等,则单词的拼写没有问题;如果不相等则出错了:时间复杂度为:O(n),操作也不方便

(2)方式二:利用二叉搜索树的方式实现:单词比根小则在左子树的位置,单词比根大则在右子树的位置。比到单词找到为止,找到叶子节点的位置都没有找到,则单词拼写错误,没有找到

typedef char DataType;//char*用于记录字符串

2、模拟实现一个简单的字典

存放单词及单词对应的中文含义(一一对应的关系),把具有一一对应关系的称之为键值对key和value具有一一对应的关系。一般来说key是唯一的是否唯一取决于具体的场景。有些场景key是可以重复的,比如一个单词对应多个意思或者容器类(multimap中键值对是可以重复的)

struct pair
{
	char *word;//单词//key
	char *Chinese;//单词对应的中文含义//value
	//比较时只需要比单词,这个字符串的大小
};

3、log文件中有许多异常重复的IP地址,请统计每个异常IP出现了多少次

将IP地址作为键值对key,将每个单词出现的次数作为value,用这个键值对建立起二叉索搜树,

也可以不存放字符串而是把字符串转换成整型的数据(一个库函数)

 atoi:把数字类型的字符串转换成整型的数据

二叉搜索树里面查找某一个元素有序(接近有序)时,二叉索搜树退化为一个线性的结构,的时间复杂度时O(n)

四、解决二叉索搜树单支的情况(不平衡的情况):平衡树

平衡树:左子树和右子树的高度之差的绝对值不能超过1(个节点下面只有一个节点时),空树也是平衡树。

AVL树的左右子树都是AVL树,左右子树高度之差,叫做平衡因子,平衡因子不超过1,则是平衡树。

平衡树也是一棵二叉搜索树。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xuruhua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值