数据结构——树:二叉排序树Binary Search Tree(BST)

本文介绍了二叉排序树(BST)的构造规则,通过递归方式按照特定大小规律插入节点。文章详细讲解了插入、查找和删除节点的函数实现,包括如何中序遍历以实现排序。提供的代码示例展示了如何使用C++实现这些操作。
摘要由CSDN通过智能技术生成

一、名称定义

BST-creating Rule:二叉排序树构造法则

root:根节点

Single Binary Tree:一元二叉树(由一个根,左右子树构成)

inOrder:中序遍历

二、理解

  二叉排序树同样是利用递归实现树的结构(Tree),只不过每个节点的构造是按照一定的大小规律进行排列的。例如:给出4,5,3,6,1,2 顺序的树,要求从左到右还是进行排列构造。定义第一个数据,即4,为root,一次从左到右开始排序。并且对于每一个一元二叉树(Single Binary Tree),左孩子的数据小于右孩子的数据,同时左孩子的数据小于root 的数据,右孩子的数据大于 root 的数据,将此规则定义为二叉排序树构造法则(BST-creating Rule),并以此开始递归。每一次新加入一个数据,都要从root 开始进行比较,大于root 中的数据,进入右孩子,小于则进入左孩子,并以此类推。

  对于上述的例子,我们构造出如下的二叉树:

以4作为root节点

3进入,比4小,进入左孩子

5进入,比4大,进入右孩子

6进入,比4大,进入右孩子(5),比5大,进入右孩子

1进入,比4小,进入左孩子(3),比3小,进入左孩子

2进入,比4小,进入左孩子(3),比3小,进入左孩子(1),比1大,进入右孩子

而排序二叉树有什么用呢?当我们中序遍历的时候(inOrder),读取到1->2->3->4->5->6

这个如果不够直观,再看下面一个例子:

 上面是一个排序二叉树,中序遍历可得:1->3->4->6->7->8->10->13->14

由此可知,利用排序二叉树+中序遍历可以对二叉树中的值进行排序。

三、代码

1.1 bstInsert():值插入函数

   这是一个最核心、最基本的函数。整个顺序二叉树的实现就是值的插入。所以只要实现了这个函数,就基本实现了顺序二叉树的建立。

typedef struct TreeNode
{
    int data;
    struct TreeNode* lchild;
    struct TreeNode* rchild;
}


void bstInsert(TreeNode**T,int data)
{
    if(*T == nullptr)
    {
        *T = (TreeNode*)malloc(sizeof(TreeNode));
        (*T)->data = data;
        (*T)->lchild = nullptr;
        (*T)->rchild = nullptr;
    }
    else if(data == (*T)->data)
    {
        return;
    }
    else if(data<(*T)->lchild)
    {
        bstInsert(&((*T)->lchild),data);
    }
    else
    {
        bstInsert(&((*T)->rchild),data);
    }

}


int main(void)
{
    TreeNode* T = nullptr;
    int nums[9] = {8,3,10,1,6,14,4,7,13};
    for(int i=0;i<9;i++)
    {
        bstInsert(&T,nums[i]);
    }
    return 0;
}

1.2 解析

  首先,数据结构就不用多说什么了,主要是对bstInsert函数的解释。传入一个node和需要插入的值。这里需要注意一下的就是main函数中的数组 nums,里面存放的是需要放入二叉树的数据。由于bstInsert 函数一次只能插入一个数据的特性,所以在main 函数中用了一个for 循环来遍历nums 里面的数据,这里强调一下。

  进入此函数首先进行判断,如果传入的是空指针,就开辟一个空间并赋值,这是为了防止段错误的产生。另一方面,就是执行BST-creating Rule 中的插入操作,即找到了最外侧的位置。不理解的可以往下继续看,下面会做解释。

  如果传入的node中的data 和需要插入的值一样,这个在BST-creating Rule 并没有定义,所以直接return 不做任何其他的操作。

  如果传入的node 中的data 大于 插入值,则将左孩子(left-child)传入函数开始递归,这个就是BST-creating Rule 里面的逻辑。

  如果传入的mode 中的data 小于 插入值, 则将右孩子(right-child)传入函数开始递归。

  当不断地递归,最后停止递归的条件就是当传入的T 为空时,就是最开始的 if 判断。

以上就是插入函数的逻辑。

2.1  bstSearch():查找某个数节点然后返回这个节点

TreeNode* bstSearch(TreeNode*T, int data)
{
    if(T)
    {
        if(T->data == data)
        {
            return T;
        }
        else if(T->data > data)
        {
            return bstSearch(T->lchild, data);
        }
        else
        {
            return bstSearch(T->rchild, data);
        }
    }
    else
    {
        return nullptr
    }
}

int main(void)
{
	TreeNode* T = nullptr;
	int nums[9] = {8,3,10,1,6,14,4,7,13};
	//this function needs you to deliver the element one by one
	//so that I use one for-circulation to achieve
	for (int i = 0; i < 9; i++)
	{
		bstInsert(&T, nums[i]);
	}
	//preOrder(T);
	inOrder(T);
	cout << endl;

	//search the value you need in the tree
	TreeNode* node = bstSearch(T, 5);
	cout << node->data << endl;

	

}

2.2 解析

  这个函数也是比较好理解的。首先在main 中调用,传入root。当T的data和查找值相同时,返回这个node。

  如果T的data大于查找值时,说明查找的值在此node的左边,所以递归传入T->lchild,同理小于也是一样的。

以上就是查找函数的逻辑。

3.1 bstDelete():删除函数,删除某个节点并返回此节点。

TreeNode* bstDelete(TreeNode* T, int data)
{
    if(T == nullptr)
    {
        return nullptr;
    }
    
    if(T->data > data) // to find
    {
        T->lchild = bstDelete(T->lchild, data);
    }
    else if(T->data < data)  // to find
    {
        T->rchild = bstDelete(T->rchild, data);
    }
    else  // to delete
    {
        if(T->lchild == nullptr && T->rchild == nullptr)
        {
            free(T); // find the node you want to delete
            return nullptr;
        }
        else if(T->lchild != nullptr && T->rchild == nullptr)
        {
            TreeNode* temp = T;
            T = T->lchild;
            free(temp);
            return T;
        }
        else if(T->rchild != nullptr && T->lchild == nullptr)
        {
            TreeNode* temp = T;
            T = T->rchild;
            free(temp);
            return T;
        }
        else
        {
            TreeNode* temp = T->rchild;
            while(temp->lchild != nullptr)
            {
                temp = temp->lchild;
            }
            T->data = temp->data;
            T->rchild = bstDelete(T->rchild, temp->data);
        }
    }
    return T;
}

3.2 解析

  首先,要判断传入的T是否为空,为空则无需删除什么,就返回一个nullptr

  下面要分三种情况。即用删除值与T->data进行对比,然后判断。

(1)若删除值大于T中的data,说明要找的node在左侧,于是对左孩子进行操作,开始递归。直到找到某一个node中的data和删除值相等,即(3)中的逻辑。

(2)若删除值小于T中的data,说明要找的node在右侧,于是对右孩子进行操作,开始递归。直到找到某一个node中的data和删除值相等,即(3)中的逻辑。

(3)在这里,就是找到了要删除的node,但是这个node的位置也是要分情况的。在边界上是否有左右孩子,以及在Tree内部。下面分情况分析:

  1-若没有左右孩子,则直接删除即可。

if (T->lchild == nullptr && T->rchild == nullptr)
        {
            free(T);
            return nullptr;
        }

 2-若有右孩子但是没有左孩子,就要把有孩子更新到此node的位置再删除。

else if (T->lchild != nullptr && T->rchild == nullptr)
        {
            TreeNode* temp = T;
            T = T->lchild;
            free(temp);
            return T;
        }

3- 若有左孩子但是没有右孩子,就要把有孩子更新到此node的位置再删除。

else if (T->lchild == nullptr && T->rchild != nullptr)
        {
            TreeNode* temp = T;
            T = T->rchild;
            free(temp);
            return T;
        }

4-若有左右孩子,这个情况就比较复杂而且是比较常见的。 这里给出的想法是,以此node的右孩子开始,找到以T->rchild为root的树的最左边的node,将其值赋值给T,再执行函数bstDelete(),删除这个最左边的node,并以此类推。其核心的思想就是,将要删除的node一个一个的移动到边界上,再删除。

else    
        {
            TreeNode* tmp = T->rchild;
            while (tmp->lchild != nullptr)
            {
                tmp = tmp->lchild;
            }
            T->data = tmp->data;
            T->rchild = bstDelete(T->rchild, tmp->data);
        }

四、完整代码框架

#include"iostream"
#include"cstdlib"
#include"cstring"
using namespace std;


/*
	Define: as for one tree, all the values on the left-child are smaller than the root
	all the values on the right-child are bigger than the root. And all the values on 
	the left-child are smaller than the values on the right-child. 

	After you create the tree in this way, you can find that when you traverse the tree 
	use the inOrder, you will get the datas which is from the smallest to the biggest.

	How to create: 
	  The whole of the creating is just inserting the value in the tree. So the main work 
	  is to achieve the function first. 
*/

typedef struct TreeNode
{
	int data;
	struct TreeNode* lchild;
	struct TreeNode* rchild;
}TreeNode;

TreeNode* bstSearch(TreeNode* T, int data)
{
	//this function is improved on the inOrder function
	if (T)
	{
		if (T->data == data)
		{
			return T;
		}
		else if (data < T->data)
		{
			return bstSearch(T->lchild, data);
		}
		else
		{
			return bstSearch(T->rchild, data);
		}
	}
	else
	{
		return nullptr;
	}
}

TreeNode* bstDelete(TreeNode* T, int data)
{
	if (T == nullptr)
	{
		return nullptr;
	}
	
	if (data < T->data)
	{
		T->lchild = bstDelete(T->lchild, data);
	}
	else if (data > T->data)
	{
		T->rchild = bstDelete(T->rchild, data);
	}
	else // when data == T->data
		//you should delete the node which is in the tree
	{
		if (T->lchild == nullptr && T->rchild == nullptr)
		{
			free(T);
			return nullptr;
		}
		else if (T->lchild != nullptr && T->rchild == nullptr)
		{
			TreeNode* tmp = T;
			T = T->lchild;
			free(tmp);
			return T;
		}
		else if (T->lchild == nullptr && T->rchild != nullptr)
		{
			TreeNode* tmp = T;
			T = T->rchild;
			free(tmp);
			return T;
		}
		else    
		{
			TreeNode* tmp = T->rchild;
			while (tmp->lchild != nullptr)
			{
				tmp = tmp->lchild;
			}
			T->data = tmp->data;
			T->rchild = bstDelete(T->rchild, tmp->data);
		}
	}
	return T;
}



void bstInsert(TreeNode** T, int data)
{
	if (*T == nullptr)
	{
		*T = (TreeNode*)malloc(sizeof(TreeNode));
		(*T)->data = data;
		(*T)->lchild = nullptr;
		(*T)->rchild = nullptr;
	}
	else if (data == (*T)->data)
	{ 
		return;
	}
	else if (data < (*T)->data)
	{
		bstInsert(&((*T)->lchild), data);
	}
	else
	{
		bstInsert(&((*T)->rchild), data);
	}
}

//void bstInsert(TreeNode** T, int data)
//{
//	//TreeNode* temp = *T;
//	TreeNode* pre = nullptr;
//	while (*T != nullptr)
//	{
//		pre = *T;
//		if (data < (*T)->data)
//		{
//			*T = (*T)->lchild;
//		}
//		else if (data > (*T)->data)
//		{
//			*T = (*T)->rchild;
//		}
//		else
//		{
//			return;
//		}
//	}
//
//	if (data < pre->data)
//	{
//		pre->lchild = (TreeNode*)malloc(sizeof(TreeNode));
//		pre->lchild->data = data;
//		pre->lchild->lchild = nullptr;
//		pre->rchild->rchild = nullptr;
//	}
//	else
//	{
//		pre->rchild = (TreeNode*)malloc(sizeof(TreeNode));
//		pre->rchild->data = data;
//		pre->rchild->lchild = nullptr;
//		pre->rchild->rchild = nullptr;
//	}
//
//
//}

void preOrder(TreeNode* T)
{
	if (T)
	{
		cout << T->data<<" ";
		preOrder(T->lchild);
		preOrder(T->rchild);
	}
}

void inOrder(TreeNode* T)
{
	if (T)
	{
		inOrder(T->lchild);
		cout << T->data << " ";
		inOrder(T->rchild);
	}
}

int main(void)
{
	TreeNode* T = nullptr;
	int nums[9] = {8,3,10,1,6,14,4,7,13};
	//this function needs you to deliver the element one by one
	//so that I use one for-circulation to achieve
	for (int i = 0; i < 9; i++)
	{
		bstInsert(&T, nums[i]);
	}
	//preOrder(T);
	inOrder(T);
	cout << endl;

	//search the value you need in the tree
	TreeNode* node = bstSearch(T, 5);
	cout << node->data << endl;

	TreeNode* del_node = bstDelete(T, 5);
	inOrder(T);


}

运行结果

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值