C语言 - 二叉排序树/二叉搜索树

版权声明:转载请注明出处 https://blog.csdn.net/qq_42292831/article/details/84652316

二叉排序树可以看做是一个较好的数据存储模型,因为在二叉排序树中有属于它自己的一套方法对数据进行高效操作:

1> 数据的链式存储

2> 数据的排序 (有序读取)

3> 查找数据(指定数据,最大值、最小值)

4> 数据的删除 (更新ing...)

本篇文章将会介绍二叉排序树的建立以及对应的一些操作;

****************************************************************************************************************************************

一:建立 ( 将数据存进二叉排序树 )

使用排序二叉树,首先需要我么将数据存储到该二叉树中,

这里首先建立一个二叉树(这里首先树的节点结构体):

typedef struct node
{
	int data;
	struct node *lchild, *rchild;
}*BinS_Tree,BinS_Tree_Node;

然后通过依次读取数据通过递归让数据自动按照下面逻辑结构布局:

大小关系如图箭头所示;

代码实现

void BinS_Tree_Insert(BinS_Tree *T, int data)
{
	if (*T == NULL)
	{
		BinS_Tree r = (BinS_Tree)malloc(sizeof(BinS_Tree_Node));
		r->data = data; r->lchild = NULL; r->rchild = NULL;
		*T = r;
		return;
	}
	else if (data < (*T)->data)
	{
		BinS_Tree_Insert(&((*T)->lchild),data);
	}
	else
	{
		BinS_Tree_Insert(&((*T)->rchild),data);
	}
}

int main()
{
	BinS_Tree T;
	T = NULL;
	srand(unsigned(time(NULL)));
	for (int i = 0; i < 10; i++)
	{
		BinS_Tree_Insert(&T, rand() % 100);
	}

注意

这里我们可以在Main函数中直接定义,然后将T赋空

BinS_Tree T;
    T = NULL;

这里还需要注意的一点是,函数的参数中使用了二级指针BinS_Tree *T

之所以这样做,是因为在该函数内(该变生存期)结束后,其一级指针的数据会被直接更改,函数内部修改后的数据也会被带出。

最简单的例子就是两个整数的交换,如果函数中的形参仅仅是变量swap(int a, int b),那么main函数中的两个数也不会被改变,将函数形参换为对应指针即可实现正常交换swap(int *a, int *b)。

但是!!

如果给了上图中的一系列数据(50,38,70,30,45,60,75,40,48,80),真的能够利用上面的源码存储成图中的逻辑结构吗?

( 忽略源码中的随机赋值,使用指定数组 );

结果是否定的,为什么呢?插入函数没错,但是在主函数中赋值的时候出现了部分纰漏:

在我们的插入函数中,因为使用的是二级指针,下图的这个地方是让每一次插入后,T都指向新插入的节点;

问题来了,这就会导致我们创建的二叉树每个节点只有一个分支,结构也偏离了我们的预期目标。

那么将这句删掉不就可以了吗?当然也是不可以的,这里之所以给T赋值是因为T初始化的时候是为空的,如果取消这一句,T就会一直保持NULL,遍历的时候就会遇到麻烦。

这时我们就会想到新的方法:

将根节点存储起来,使得T一直指向根节点,然后在每次调用之前都对T进行一次根节点赋值,代码如下:

int main()
{
	BinS_Tree T,root;
	T = NULL;
	root = NULL;
	srand(unsigned(time(NULL)));
	for (int i = 0; i < 10; i++)
	{
		if (i == 0)
		{
			BinS_Tree_Insert(&T, rand() % 100);
			root = T;
		}
		else
		{
			T = root;
			BinS_Tree_Insert(&T, rand() % 100);
		}
	}
}

二:查询(指定数据,最大值、最小值)

1> 查询最大值、最小值

void Search_MinData(BinS_Tree T)
{
	if (!T)
		return;
	if (T->lchild == NULL)
	{
		cout << "The MinData is : " << T->data << endl;
	}
	else
	{
		Search_MinData(T->lchild);
	}
}
void Search_MaxData(BinS_Tree T)
{
	if (!T)
		return;
	if (T->rchild == NULL)
	{
		cout << "The MaxData is : " << T->data << endl;
	}
	else
	{
		Search_MinData(T->rchild);
	}
}

因为数据的存储结构是有一定规律的 (见上图),这里使用递归进行查询就是寻找最左边的叶子和最右边的叶子;

2> 查询指定数据

bool Search_Data(BinS_Tree T, int data)      
{
	bool Find = false;
	if (!T)
		return false;
	if (T->data == data)
	{
		Find = true;
		//cout << "Search Successful!" << data << endl;
		return Find;
	}
	else if(data > T->data)
	{
		Search_Data(T->rchild, data);
	}
	else
	{
		Search_Data(T->rchild, data);
	}
	//if (!Find)    //这一因为执行递归进程,所以会在这里调用很多次,可以取消在函数中直接判断,而是在函数外部通过该函数的返回值进行判断
	//{
	//	cout << "Cannot fine the number : " << data << "!" << endl; 
	//}
	return Find;
}
void Judge_Search_Data(bool Judge, int data)
{
	if (Judge)
	{
		cout << "Data(" << data << ") in this zone !" << endl;
	}
	else
	{
		cout << "Cannot find the Data(" << data << ")" << endl;
	}
}

三:数据的排序 (有序读取)

因为数据逻辑结构存的特殊性,使用二叉树的中序遍历可以直接将数据从小到大进行输出:

void Traverse_Middle(BinS_Tree T)
{
	if (!T)
	{
		return;
	}
	else
	{
		Traverse_Middle(T->lchild);
		cout << T->data << endl;
		Traverse_Middle(T->rchild);
	}
}

从大到小更改左右子树递归的顺序即可:

void Traverse_Middle(BinS_Tree T)
{
	if (!T)
	{
		return;
	}
	else
	{
		Traverse_Middle(T->rchild);
		cout << T->data << endl;
		Traverse_Middle(T->lchild);
	}
}

四:删除

这张图的五个圈分别代表删除数据时遇到的的五种情况:叶子、只有左子树、只有右子树、左右子树都有和根节点

五:源码(测试用)

#include <bits/stdc++.h>
using namespace std;

typedef struct node
{
	int data;
	struct node *lchild, *rchild;
}*BinS_Tree,BinS_Tree_Node;

bool Search_Data(BinS_Tree T, int data)      
{
	bool Find = false;
	if (!T)
		return false;
	if (T->data == data)
	{
		Find = true;
		//cout << "Search Successful!" << data << endl;
		return Find;
	}
	else if(data > T->data)
	{
		Search_Data(T->rchild, data);
	}
	else
	{
		Search_Data(T->rchild, data);
	}
	//if (!Find)    //这一因为执行递归进程,所以会在这里调用很多次,可以取消在函数中直接判断,而是在函数外部通过该函数的返回值进行判断
	//{
	//	cout << "Cannot fine the number : " << data << "!" << endl; 
	//}
	return Find;
}
void Judge_Search_Data(bool Judge, int data)
{
	if (Judge)
	{
		cout << "Data(" << data << ") in this zone !" << endl;
	}
	else
	{
		cout << "Cannot find the Data(" << data << ")" << endl;
	}
}

void Search_MinData(BinS_Tree T)
{
	if (!T)
		return;
	if (T->lchild == NULL)
	{
		cout << "The MinData is : " << T->data << endl;
	}
	else
	{
		Search_MinData(T->lchild);
	}
}
void Search_MaxData(BinS_Tree T)
{
	if (!T)
		return;
	if (T->rchild == NULL)
	{
		cout << "The MaxData is : " << T->data << endl;
	}
	else
	{
		Search_MinData(T->rchild);
	}
}

void BinS_Tree_Insert(BinS_Tree *T, int data)
{
	if (*T == NULL)
	{
		BinS_Tree r = (BinS_Tree)malloc(sizeof(BinS_Tree_Node));
		r->data = data; r->lchild = NULL; r->rchild = NULL;
		*T = r;
		return;
	}
	else if (data < (*T)->data)
	{
		BinS_Tree_Insert(&((*T)->lchild),data);
	}
	else
	{
		BinS_Tree_Insert(&((*T)->rchild),data);
	}
}

void Traverse_Middle(BinS_Tree T)
{
	if (!T)
	{
		return;
	}
	else
	{
		Traverse_Middle(T->lchild);
		cout << T->data << endl;
		Traverse_Middle(T->rchild);
	}
}

int main()
{
	BinS_Tree T,root;
	T = NULL;
	root = NULL;
	srand(unsigned(time(NULL)));
	for (int i = 0; i < 10; i++)
	{
		if (i == 0)
		{
			BinS_Tree_Insert(&T, rand() % 100);
			root = T;
		}
		else
		{
			T = root;
			BinS_Tree_Insert(&T, rand() % 100);
		}
	}
	Traverse_Middle(T);
	int data = rand() % 10;
	bool Judge = Search_Data(T, data);
	Judge_Search_Data(Judge,data);
	Search_MinData(T);
	Search_MaxData(T);
	return 0;
}

****************************************************************************************************************************************

             最快的脚步不是跨越,而是继续,最慢的步伐不是小步,而是徘徊。
 

****************************************************************************************************************************************

猜你喜欢

转载自blog.csdn.net/qq_42292831/article/details/84652316