BST---二叉搜索树

二叉查找树(Binary Search Tree),也称二叉搜索树、有序二叉树(ordered binary tree),二叉排序树(orted binary tree),是指一棵空树或者具有下列性质的二叉树:

若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
任意节点的左、右子树也分别为二叉查找树;
没有键值相等的节点
二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低,均为O(log n)。二叉查找树是基础性数据结构,用于构建更为抽象的数据结构,如集合、multiset、关联数组等。

头文件
#include<windows.h>
#include<assert.h>
#include<limits.h>
#include<iostream>
#include<vector>
#include<time.h>
#include<stack>
#include <queue>
using namespace std;

结构体
线索二叉树(二叉搜索树)(四个域:双亲、左、右、数据)
二叉排序树

typedef int KType;
#define END '#'

typedef struct BstNode
{
	BstNode * left;
	BstNode * right;
	BstNode *parent;
	KType key;
}BstNode;

typedef struct
{
	BstNode * head;
	int cursize;
}BSTree;

1. 查找

在二叉搜索树bb中查找xx的过程为:

若b是空树,则搜索失败,否则:
若x等于b的根节点的数据域之值,则查找成功;否则:
若x小于b的根节点的数据域之值,则递归搜索左子树;否则:
递归查找右子树

//1、非递归查询kx值的操作

BstNode * FindValue(BSTree &myt,KType kx)
{
	BstNode *p=myt.head->parent;//指向根节点
	while(p !=NULL && p->key !=kx)
	{
		p=kx <p->key ?p->left:p->right;
	}
	return p;
}

//2、递归查询kx值的操作
BstNode * Value(BstNode *p,KType kx)
{
	if(p==NULL || p->key==kx)
		return p;
	else if(kx> p->key)
	{
		return Value(p->right,kx);
	}
	else
		return Value(p->left,kx);
}

BstNode * SearchValue(BSTree &myt,KType kx)  //类型为BSTree 所以要封装一层
{
	return Value(myt.head->parent,kx); 
}
2. 插入

向一个二叉搜索树bb中插入一个节点ss的算法,过程为:

若b是空树,则将s所指结点作为根节点插入,否则:
若s.val等于b的根节点的数据域之值,则返回,否则:
若s.val小于b的根节点的数据域之值,则把s所指节点插入到左子树中,否则:
把s所指节点插入到右子树中(新插入节点总是叶子节点)

bool InsertBST(BSTree &myt,KType kx)
{
	BstNode *pa = myt.head ; // head;
	BstNode *p = myt.head->parent;// root; //插入时,一个指针不行,因为一个指针动了之后无法找到给哪个结点插,应该用两个指针,一前一后插入

	while(p != NULL && p->key != kx)//找kx值,退出条件为(p为空或找到值为kx的结点)
	{
		pa = p;
		p = kx < p->key ? p->left: p->right;
	}

	if(p != NULL)  return false; //退出循环是如果p不为空,则找到值为kx的结点,但是BST树不允许key值重复,所以插入失败
	p = Buynode(pa); //购买结点,双亲指向pa
	p->key = kx;
	
	if(pa==myt.head)//判断是否空树
	{
		myt.head->left=p;
		myt.head->right=p;
		myt.head->parent=p;
	}
	else
	{
		if(p->key  < pa->key )
		{
			pa->left=p;
			if(p->key  < myt.head->left  ->key)  //更改最小值left的指针指向
			{
				myt.head ->left=p;
			}
		}
		else
		{
			pa->right=p;
			if(p->key  > myt.head->right->key) //更改最大值right的指针指向
			{
				myt.head ->right=p;
			}
		}
	}
	
	myt.cursize+=1;
	return true;
}

int GetSize(BSTree &myt)   //计算结点个数
{
	return myt.cursize;
}

bool Empty(BSTree &myt)
{
	return GetSize(myt); //个数为0就是空树
}

3. 删除

二叉搜索树的删除操作分三种情况讨论: 
1. 如果待删除的节点是叶子节点,那么可以立即被删除,如下图所示: 
例:删除数据为16的节点,是叶子节点,可以直接删除
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190711175707707.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzQwNzU3Nw==,size_16,color_FFFFFF,t_70)

2. 如果有一个子节点,要将下一个子节点上移到当前节点,即替换之 
例:删除数据为25的节点,它下面有唯一一个子节点35, 上移到替换之 

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190711175733516.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzQwNzU3Nw==,size_16,color_FFFFFF,t_70)
3. 如果有两个子节点,则将其右子树的最小数据代替此节点的数据,并将其右子树的最小数据删除,如下图所示 
例:删除节点数据为5的节点,找到被删除节点右子树的最小节点。需要一个临时变量successor,将11节点下面的子节点进行查询,找到右子树最小节点7,并把右子树最小节点7替换被删除节点,维持二叉树结构。如下图 

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190711175750547.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzQwNzU3Nw==,size_16,color_FFFFFF,t_70)
//删除元素 (叶子、单分支、双分支、头)
bool RemoveBST(BSTree &myt,KType kx)
{
	if(Empty(myt))   return false;
    BstNode *p= FindValue(myt,kx);
    if(p == NULL) return false;
  /

	if(p->left != NULL && p->right != NULL)   //双分支(找到kx后左右子树都不为空的情况)
	{ 
		BstNode *nt = Next(myt.head,p->right);  //找后继
		p->key = nt->key; //用后继值覆盖p值
		p = nt;
	}
  / //

	BstNode *pa = p->parent;     //删除叶子和单分支结点

	BstNode * child = p->left != NULL? p->left:p->right;  //p为单分支child指向left和right一种,p为叶子,child指向NULL
	if(child !=NULL) child->parent=pa;    //将child 的 parent 指向p的前驱

	if(pa==myt.head )                     //判断p是否指向头
	{
		myt.head->parent=child;
	}

	else  //不是头结点
	{
		if(pa->left ==p)     //将p的前驱的后继指向p的后继(child)
		{
			pa->left =child;
		}
		else
		{
			pa->right=child;        
		}
	}
//
	FreeNode(p);
	myt.cursize-=1;
	return true;
}
4. 遍历

可以采用前序,中序,后序来遍历该二叉搜索树,或者使用广度优先搜索的方式。这里用中序遍历来实现,可以保证按从小到大的顺序打印。
BstNode * First(BstNode *p)//找第一个最左边
{
	while(p != NULL && p->left != NULL)
	{
		p = p->left;
	}
	return p;
}
BstNode * Next(BstNode *head,BstNode *p) //找后继
{
	if(NULL == p) return NULL;
	if(p->right != NULL)
	{
		return First(p->right);
	}
	else//右子树为空
	{
		//parent结点就是下一个结点
		BstNode *pa = p->parent;
		while(pa != head && pa->left != p)
		{
			 //不断向上指向
			p = pa;
			pa = pa->parent;
		}
		if(pa == head)
		{
			pa = NULL;
		}
		return pa;
	}
}
void NiceInOrder(BSTree &myt)//从小到大
{
	for(BstNode *p = First(myt.head->parent);
		p != NULL; p = Next(myt.head,p))
	{
		cout<<p->key<<" ";
	}
	cout<<endl;
}


BstNode *Last(BstNode *ptr) //  找最后一个,找最大值
{
	while(ptr != NULL && ptr->right != NULL)
	{
		ptr = ptr->right;
	}
	return ptr;
}
BstNode * Prev(BSTree &myt,BstNode *ptr) //找前驱
{
	if(ptr == NULL || ptr == myt.head) return NULL;
	if(ptr->left != NULL)
	{
		return Last(ptr->left);
	}
	else
	{
		BstNode *pa = ptr->parent;
		while(pa != myt.head && pa->right != ptr)
		{
			ptr = pa;
			pa = pa->parent;
		}
		if(pa == myt.head)
		{
			pa = NULL;
		}
		return pa;
	}
}
void ResNiceInOrder(BSTree &myt) //从大到小
{
	for(BstNode *p = Last(myt.head->parent); 
		p != NULL; p = Prev(myt,p))
	{
		cout<<p->key<<" ";
	}
	cout<<endl;
}

5. 构造一颗二叉查找树

用一组数值建造一棵二叉查找树的同时,也把这组数值进行了排序。其最差时间复杂度为 O(n2)O(n2)。例如,若该组数值经是有序的(从小到大),则建造出来的二叉查找树的所有节点,都没有左子树

//创建一个节点
BstNode * Buynode(BstNode *pa=NULL,BstNode *leftchild =NULL,BstNode *rightchild =NULL )
{
	BstNode *s=(BstNode *)malloc(sizeof(BstNode));
	if(s== NULL)
	{
		exit(1);
	}
	s->left=leftchild;
	s->right=rightchild;
	s->parent = pa;
	/*memset(s,0,sizeof(BstNode));*/
	return s;
}

//释放一个节点
void FreeNode(BstNode *s)
{
	free(s);
}

//初始化一棵树
void InitBSTree(BSTree &myt)
{
	myt.head = Buynode();//购买一个头结点
	myt.cursize = 0;//头结点中的数据是随机值
}
//销毁操作
void DestroyBSTree(BSTree &myt);


性能分析

查找:最佳情况Olog(n)Olog(n), 最坏情况O(n)O(n) 
插入:最佳情况Olog(n)Olog(n), 最坏情况O(n)O(n) 
删除:最佳情况Olog(n)Olog(n), 最坏情况O(n)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值