二叉搜索(排序)树--BST的基本原理与概念及代码实现

二叉排序树

  • 思考:

                      给定一组数列{7,3,10,12,5,1,9},要求能够高效的完成对数据的查询和添加

我们可能有以下解决方案:

1.使用数组

  • (1)数组未排序,优点:直接在数组尾添加,速度快。缺点:查找速度慢.
  • (2)数组排序,优点:可以使用二分查找,查找速度快,缺点:为了保证数组有序在添加新数据时,找到插入位置后,后面的数据需整体移动,速度慢。 

 2.使用链式存储--链表

  • 不管链表是否有序,查找速度都慢,添加数据速度比数组快,不需要数据整体移动。

 

 3.使用二叉排序树

 二叉排序树介绍:

二叉排序树:BST:( Binary Sort( Search)Tree),

1.要么是一棵空树

2.要么对于二叉排序树的任何一个非叶子节点要求

          左子节点的值比当前节点的值小

          右子节点的值比当前节点的值大

特别说明:如果有相同的值,可以将该节点放在左子节点或右子节点

比如:针对前面的数据(7,3,10,12,5,1,9),对应的二叉排序树为: 

 

                                                        

 

解释:

  •  首先它先存入7,然后发现3比7小,所以把3作为它的左节点
  • 发现10比7大,放在它的右子节点
  • 12比7大,然后和10比,同样比10大,所以放在10的右子节点
  • 此类推

 

性质:按照中序遍历把节点打印,将得到由小到大的排列

 

二叉搜索树的结构:

typedef struct NODE                     
{
	int key;
	struct node* left;
	struct node* right;
}node,*bst;

 

关于二叉搜索树的基本操作:

  •  查找:

  1. 当节点root为NULL的时候说明没有找到,查找失败,返回NULL;
  2. 如果想要查找的值x等于当前根结点数据域root->data,说明查找成功,访问该节点,输出该值root->key
  3. 如果想要查找的值x小于当前根结点数据域root->data,说明应该往左子树查找,所以向root->lchild递归
  4. 如果想要查找的值x大于当前根结点数据域root->data,说明应该往右子树查找,所以向root->rchild递归

bst find(int x, bst root)                //查找操作,这里我们没有对传入的指针进行修改,故不需要使用引用!
{
	if (root == NULL)
		return NULL;
	if (root->key < x)
		return find(x, root->right);
	else if (root->key > x)
		return find(x, root->left);
	else
		return root;
}

 

插入:

  1. 如果树为空则创建一棵树,该值作为起点
  2. 如果相等则return,(二叉搜索树不能有相同的值,但是有的版本说如果真的有相同的值可以插入左边或者右边,但是依旧要保持左小右大的原则)
  3. 如果大于就递归右子树
  4. 如果小于就递归左子树

void insert(node* &root,int x){  //insert函数将在二叉树中插入一个数据域为x的新结点(注意参数root要加引用&)
	if(root==NULL){
		root=newNode(x);
		return;
	}
	if(x==root->data){
		return ;
	}
	else if(x<root->data){
		insert(root->lchild,x);
	}
	else{
		insert(root->rchild,x);
	}
} 

 

其他:

当我们有了插入函数之后,我们也可以通过插入函数进行创建二叉搜索树:

其中参数是要创建树的数组


node* Create(int data[],int n){
	node* root=NULL;
	for(int i=0;i<n;i++){
		insert(root,data[i]);
	}
	return root;
}

 

删除:

  1. 把以二叉查找树中比结点权值小的最大结点称为该结点的前驱,而把比权值大的最小结点称为该结点后继。显然,结点的前驱是该结点左子树中的最右结点,而结点的后继是右子树中的最左结点。
  2. 如果当前结点root为空,说明不存在权值为给定权值x的结点,直接返回
  3. 如果当前结点root的权值恰好为给定权值x,说明找到了想要删除的结点,此时进行删除处理:a)如果当前结点root不存在左右孩子,说明是叶子结点,直接删除;b)如果当前结点存在左孩子,那么在左子树中寻找结点前驱pre,然后让pre的数据覆盖root,接着删除左子树中结点pre;c)如果当前结点存在右孩子,那么在右子树中寻找结点后继next,然后让next的数据覆盖root,接着删除右子树中结点next;
  4. 如果当前结点root的权值大于给定权值x,则在左子树中递归删除权值为x的结点
  5. 如果当前结点root的权值小于给定权值x,则在右子树中递归删除权值为x的结点

 


void deleteNode(node* &root,int x){
	if(root==NULL) return ;
	if(root->data==x){
		if(root->lchild==NULL && root->rchild==NULL){
			root=NULL;
		}
		else if(root->lchild!=NULL){
			node* pre=findMax(root->lchild);
			root->data=pre->data;
			deleteNode(root->lchild,pre->data);
		}
		else{
			node* next=findMin(root->rchild);
			root->data=next->data;
			deleteNode(root->rchild,next->data);
		}
	}
	else if(root->data>x){
		deleteNode(root->lchild,x);
	}
	else{
		deletdNode(root->rchild,x);
	}
} 

 

最大值最小值:

通过以上的学习就可以知道  最小值就左边的数,最大值就是最右边的数  (所以递归到最左边/右边就ok)  这里就不多说了;


//寻找以root为根结点的树中的最大权值结点
node* findMax(node* root){
	while(root->rchild!=NULL){
		root=root->rchild;
	}
	return root;
} 
//寻找以root为根结点的树中的最小权值结点
node* findMin(node* root){
	while(root->lchild!=NULL){
		root=root->lchild;
	}
	return root;

 

遍历:中序的遍历结果是有序的

关于遍历可以看我的其他两篇文章:

小白开始学二叉树:https://blog.csdn.net/alzzw/article/details/97283324

线索二叉树:https://blog.csdn.net/alzzw/article/details/97423394

哈夫曼树:https://blog.csdn.net/alzzw/article/details/97809047

平衡二叉树:https://blog.csdn.net/alzzw/article/details/97613193

B树~B+树:https://blog.csdn.net/alzzw/article/details/97633941

红黑树:https://blog.csdn.net/alzzw/article/details/97770753

                                                    完

参考:https://blog.csdn.net/object__/article/details/81916364

 

 

 

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿尔兹

如果觉得有用就推荐给你的朋友吧

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

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

打赏作者

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

抵扣说明:

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

余额充值