二叉树学习(二)之二叉查找树

本文介绍了二叉查找树的概念、特点及操作,包括如何创建二叉查找树、查找特定值、插入新节点以及删除节点。二叉查找树的中序遍历为升序序列,其特点是左子树所有节点值小于根节点,右子树所有节点值大于根节点。文章还探讨了查找最大值和最小值的方法,并预告了后续将要学习的二叉平衡树等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面学习过二叉树的基本遍历与创建,今天学习二叉树中的二叉查找树,也叫二叉搜索树。

一、二叉搜索树的特点

1、若左子树不为空,则左子树上所有结点的值均小于它的根节点的值;多右子树不为空,则右子树上所有结点的值均大于它的根节点的值;

2、二叉查找树的左右子树也是一颗二叉查找树。

3、二叉查找树的中序遍历是一个升序序列。

4、二叉查找树没有键值相等的节点。

二、二叉查找树的创建。

一、思路:

当我们有一个无序数组,且数组的元素不重复,根据此数组创建一颗二叉查找树。

(1)在数组中任意选取一个元素作为根节点(为了方便我们取第一个元素)

(2)创建一个节点P,并根据该节点的数据值(data),在已有的二叉查找树中寻找插入位置。

         1、对于每一个结点都有OLDP,P->data大于OLDP-data则该节点向右结点搜寻,即OLDP = OLDP->right;

              若P->data小于 OLDP-data则该节点向左结点搜寻OLDP = OLDP->left。

         2、循环实现1 直到OLDP == NULL ,说明P结点已经寻找到需要插入的叶子结点。

         3、因为OLDP已经为NULL,不能插入,我们需要寻找的是叶子结点,因此需要使用一个节点LAST,

               LAST结点满足 LAST->right/left = OLDP

(3)对于每一个数组元素都需要进行第2步,直到数组元素全部存储。

二、代码:

Node *CreatTree(DataType *a, int N)
{
	Node *root = (Node*)malloc(sizeof(Node));
	Node *P = NULL,*Last = NULL,*Path = root;
	root -> data = a[0];
	root->left = NULL;
	root->right = NULL;
	if (root&&a&&N>0)
	{
		for (int i = 1; i < N; i++)
		{
			P = (Node*)malloc(sizeof(Node));
			P->data = a[i];
			P->left = NULL;
			P->right = NULL;
			Path = root;
			while (Path)  //用来寻找适合P结点插入的叶子结点Last
			{
				Last = Path;
				if (P->data > Path->data)
				{
					Path = Path->right;
				}
				else
				{
					Path = Path->left;
				}
			}
			if (P->data>Last->data)
			{
				Last->right = P;
			}
			else
			{
				Last->left = P;
			}
		}
		return root;
	}
	else
	{
		return root = NULL;
	} 
}

三、二叉查找树的查找。

(一)查找指定的值

查找为data结点,找到后并返回结点的指针。

思路:因为对于二叉查找树的任意一个结点都有,该节点的左结点的值<根节点的值<右结点的值,因此我们可如此:如果data>P->data那么P = P->right;如果data < P->data 则,P=P->left。直到P == NULL 或 P ->data == data。

代码:

/***************************************************
函数功能:查找二叉树中值为data的结点
函数参数:root:二叉查找树的根节点
		  data:需要查找的值
函数返回值 如果找到返回结点指针,没有找到到返回NULL
****************************************************/
Node *FindData(Node *root, DataType data)
{
	if (!root)
		return NULL;
	if (data == root->data)
	{
		return root;
	}
	if (data<root->data)
	{
		FindData(root->left, data);
	}
	else
	{
		FindData(root->right, data);
	}
}

(二)查找最大值/最小值

思路:二叉排序树的最大值一定是最右侧的结点的值,最小值一定是最左侧的结点的值。因此我们用递归遍历二叉树P = P->left/right;当且仅当P->left/right == NULL 时,递归结束,此时P为二叉树的最小 / 最大值。

代码:

最小值:

/************************************
函数功能:查找二叉查找树的最小值结点
函数参数:root:二叉树的根节点
函数返回值:最小值结点的指针
*************************************/

Node *FindMin(Node *root)
{
	if (!root)
		return NULL;
	if (!root->left)
	{
		return root;
	}
	else
	{
		FindMin(root->left);
	}
}

最大值:

/************************************
函数功能:查找二叉查找树的最大值结点
函数参数:root:二叉树的根节点
函数返回值:最大值结点的指针
*************************************/
Node *FindMax(Node *root)
{
	if (!root)
		return NULL;
	if (!root->right)
	{
		return root;
	}
	else
	{
		FindMax(root->right);
	}
}

(二)查找指定值的结点的上一个结点

/****************************************************************
函数功能 : 查找指定值结点的根结点。
函数参数 : root :二叉树的根节点
			data :指定的数据
函数返回值 : 找到返回上一个结点的指针,没有找到返回NULL
***********************************************************/
Node *FindLastNode(Node *root, DataType data)
{
	if (!root)
		return NULL;
	Node *Plast = NULL,*Pnow = root;

	while (Pnow)
	{
		if (data == Pnow->data && Pnow != root)   //根节点没有上一个结点
		{
			return Plast;
		}
		else if (data>Pnow->data)
		{
			Plast = Pnow;
			Pnow = Pnow->right;
		}
		else if (data < Pnow->data)
		{
			Plast = Pnow;
			Pnow = Pnow->left;
		}
		//一次while循环结束时若Pnow还和root相等则
		//说明找到了根节点,根节点没有上一个结点
		if (Pnow == root)  
		{
			return NULL;
		}
	}
}

四、二叉查找树的插入。

二叉树的插入一定是在结点的左子树或右子树为空的地方进行插入,因此我们如下思路:

1、对插入的数据进行结点生成,分配内存。

2、寻找待插入的位置。

      (1) 如果data< P->data 则使 P = P->left; 

        (2)    如果data > P ->data 则使P = P->right;

        (3)    循环上面步骤,直到P = NULL;

3、记住P(此时P为NULL)的上一个结点进行插入。

/*******************************************
函数功能 :将指定的值插入到二叉树的合适位置
函数参数 :root : 二叉树的值
		   data :需要插入的值
函数返回值:true : 成功
		   false :错误
********************************************/
bool InseartNode(Node *root, DataType data)
{
	if (!root)
		return false;
	Node *Plast = root,*Pnow = (Node*)malloc(sizeof(Node));
	Pnow->left = NULL;
	Pnow->right = NULL;
	Pnow->data = data;   
	//当root=NULL时,已经找到了要插入的位置。
	while (root)
	{
		Plast = root;
		if (data <root->data)
		{
			root = root->left;
		}
		else if (data>root->data)
		{
			root = root->right;
		}
	}
	if (Pnow ->data < Plast->data)
	{
		Plast->left = Pnow;
		return true;
	}
	else if (Pnow->data > Plast->data)
	{
		Plast->right = Pnow;
		return true;
	}
	else
		return false;
}

五、二叉查找树的结点删除。

1、第一种情况当被查找到的节点是树叶。

2、第二种情况查找到的树有一个节点。

3、第三种情况查找到的树有两个节点。

4、如果什么都没有查到应该返回false。

思路:我们这里删除某个节点Pnow,的本质,是在Pnow的子树里面选取一个结的值,并将该节点的值赋给Pnow,再将选取的结点进行删除的思路。下面我们所有有的删除都会围绕这个思想去进行。

bool DeleteNode(Node *root, DataType data)
{
	if (!root)
		return true;
	Node *Pnow = FindData(root, data);       //找到要删除的结点。
	Node *Pmin = NULL;
	Node *PminLast = NULL;
	Node *Ptmp = NULL;
	if (Pnow)    //如果没有找到则,删除失败
	{
		//要删除的结点至少存在一个子结点
		if (Pnow->left || Pnow->right)			 
		{
			//被删除的结点左右结点都存在。
			if(Pnow->left&&Pnow->right)        
			{
				//Pmin该结点的左子树一定为NULL(因为他是右子树中的最小值)
				Pmin = FindMin(Pnow->right); 
				//找到最小值结点的上一个结点
				PminLast = FindLastNode(Pnow, Pmin->data); 
				//将最小值结点的值赋给被删除的结点的值,类似于删除了要删除的结点。
				Pnow->data = Pmin->data;
				//当最小值结点的上一个结点就是要删除的结点时,
				//那么最小值结点一定是删除结点Pnow的右子结点。
				//当上一个结点不是要删除的结点时,Pmin一定是PminLast的左子结点
				//Pmin次时左结点一定为NULL,但是右结点不一定为NULL。
				if (PminLast == Pnow)
				{
					//将最小值结点的右子结点与要被删除Pnow结点的右子结点挂。
					Pnow->right = Pmin->right;
				}
				else
				{
					//将最小值结点的右子结点与上一个结点的左子挂上。
					PminLast->left = Pmin->right;
				}
				//断开最小值结点的右子结点
				Pmin->right = NULL;
				free(Pmin);
				Pmin = NULL;
				return true;
			}
			else
			{
				if (Pnow->left&&!Pnow->right)        //被删除的结点只有左子结点
				{
					Ptmp = Pnow->left;
				}
				else if (!Pnow->left&&Pnow->right)   //被删除的结点只有右子结点
				{
					Ptmp = Pnow->right;
				}
				Pnow->data = Ptmp->data;
				Pnow->data = Ptmp->data;
				Pnow->left = Ptmp->left;
				Pnow->right = Ptmp->right;
				Ptmp->left = NULL;
				Ptmp->left = NULL;
				free(Ptmp);
				Ptmp = NULL;
				return true;
			}
		}
		else    //要删除的点没有子结点
		{
			Node *Plast = FindLastNode(root, data);
			if (Plast)
			{
				if (Plast->left == Pnow)
					Plast->left = NULL;
				else
					Plast->right = NULL;
				return true;
			}
			else  //该树只有根节点
			{
				free(Pnow);
				Pnow = NULL;
				return true;
			}
		}
	}
	else
	{
		cout << "没有找到值为" << data << "的结点" << endl;
		return false;
	}
}

六、感言

二叉查找树的增删改查,仅仅只是学习二叉树的开始,只有回了基础,才能有更高的进步,后面我会继续学习关于二叉平衡树、以及他的几种实现方法,比如:红黑树,VAL树、替罪羊树、Treap、伸展树等等。加油!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值