二叉搜索树

基础知识

性质

二叉搜索树又叫二叉排序树,其或者是一棵空树。
性质:

  • 每个结点都有一个作为搜索依据的关键码(key),所有结点的关键码各不相同。
  • 左子树(如果存在)上所有节点的关键码都小于根节点的关键码
  • 右子树(如果存在)上所有结点的关键码都大于根节点的关键码。
  • 左子树和右子树也是二叉搜索树

举例理解

在这里插入图片描述
如上三图,如果一颗而擦函数进行中序遍历,可以按从小到大的顺序,将各节点关键码排序起来,所以也称二叉搜索树为二叉排序树。

结构体

一棵二叉搜索出存在三个指针,左右孩子和双亲指针,还有一个关键码。

typedef int KeyType;
typedef struct BstNode {
	struct BstNode* leftchild;
	struct BstNode* parent;
	struct BstNode* rightchild;
	KeyType key;
}BstNode;
typedef struct {
    BstNode* root;
	int cursize;
}BStree;

函数编写

搜索查找

从二叉排序树中查找一个关键码:我们知道左子树的关键码必然小于根节点的关键码,根节点的关键码必然小于右孩子的关键码。所以遍历时仅仅需要判断要查找的关键是否大于根节点的关键码,大于在右边找,继续判断右子树的根节点关键码与要查找的关键码大小,反之在左子树进行查找。

非递归

BstNode* FindValue(BSTree* ptree, KeyType kx) {
	BstNode* ptr = ptree->root;
	while (ptr!=nullptr&&kx!=ptr->key){
		ptr = kx < ptr->key ? ptr->leftchild : ptr->rightchild;
	}
	return ptr;
}

递归

BstNode* Search(BstNode* ptr, KeyType kx) {
	if (ptr == nullptr || ptr->key == kx) return ptr;
	else if (kx < ptr->key)return Search(ptr->leftchild, kx);
	else return Search(ptr->rightchild,kx);
}
BstNode* Searchvalue(BSTree* ptree, KeyType kx) {
	return Search(ptree->root, kx);
}

插入数据

BstNode* Buynode() {//购买新节点
	BstNode* s = (BstNode*)calloc(1, sizeof(BstNode));
	if (nullptr == s) exit(EXIT_FAILURE);
	return s;
}
bool Insert_Item(BSTree* ptree,KeyType kx) {
	assert(ptree!=nullptr);
	if (ptree->root ==nullptr) {//判断不存在根节点
		ptree->root = Buynode();
		ptree->root->key = kx;
		ptree->cursize = 1;
			return true;
	}
	BstNode* ptr = ptree->root, * pa = nullptr;
	while (ptr!=nullptr && ptr->key != kx) {//遍历,遍历结束找出该结点本应该存放的双亲结点
		pa = ptr;
		ptr = kx > ptr->key ? ptr->rightchild : ptr->leftchild;
	}
	if (ptr != nullptr && ptr->key == kx) return false;//树中原本就存在这个数据,直接返回错误
	//树中不存在该结点进行插入
	ptr = Buynode();//购买新节点
	ptr->key = kx;//新节点关键码赋值
	ptr->parent = pa;//双亲指针赋值
	//判断双亲结点的关键码与要插入的值的大小比较
	if (ptr->key < pa->key) {
		pa->leftchild = ptr;
	}
	else {
		pa->rightchild = ptr;
	}
	ptree->cursize += 1;//节点个数加一
	return true;
}

中序遍历

递归

void InOrder(BstNode* ptr) {
	if (ptr != nullptr) {
		InOrder(ptr->leftchild);//遍历左子树
		printf("%d ", ptr->key);
		InOrder(ptr->rightchild);//遍历右子树
	}
}
void InOrder(BSTree* ptree) {
	assert(ptree != nullptr);
	InOrder(ptree->root);
	printf("\n");
}

非递归

BstNode* First(BstNode* ptr){//寻找中序遍历的第一个结点
	while (ptr!=nullptr&&ptr->leftchild!=nullptr) {//一直寻找最左边的结点即可
		ptr = ptr->leftchild;
	}
	return ptr;
}
BstNode* Next(BstNode* ptr) {//寻找某节点中序遍历的后继节点
	if (nullptr == ptr)return nullptr;
	if (ptr->rightchild != nullptr) {//右孩子不等于null,说明其后继结点是右子树中序遍历的第一个结点
		return First(ptr->rightchild);
	}
	else {//右子树为null分为两种情况,第一种是该结点是其双亲结点的左子树,那么直接后继便是其双亲结点。
		//第二种是该结点是其双亲结点的右孩子,那么就要回溯,直到找到一棵树是其根节点的左孩子,此时根节点便是直接后继
		BstNode* pa = ptr->parent;
		while (pa!=nullptr&& pa->leftchild != ptr) {//此处pa!=null,为了出现最后一个结点的情况
			ptr = pa;
			pa = ptr->parent;
		}
		return pa;
	}
}
void NiceInOrder(BSTree* ptree) {
	for (BstNode* ptr = First(ptree->root); ptr != nullptr; ptr = Next(ptr)) {//从第一个结点一直遍历直到最后一个
		printf("%d ",ptr->key);
	}
}
}

逆序打印中序遍历

此处和中序遍历打印相对应,可以通过以上代码理解思考

非递归

BstNode* Last(BstNode* ptr) {//寻找中序遍历最后一个结点
	while (ptr != nullptr && ptr->rightchild != nullptr) {
		ptr = ptr->rightchild;
	}
	return ptr;
}
BstNode* Prev(BstNode* ptr) {
	if (ptr == nullptr)return nullptr;
	if (ptr->leftchild != nullptr) {
		return Last(ptr->leftchild);
	}
	else {
		BstNode* pa = ptr->parent;
		while (pa != nullptr && pa->rightchild != ptr) {
			ptr = pa;
			pa = ptr->parent;
		}
		return pa;
	}
}
void ResNiceInOrder(BSTree* ptree) {
	assert(ptree != nullptr);
	for (BstNode* ptr = Last(ptree->root); ptr != nullptr; ptr = Prev(ptr)) {
		printf("%d ", ptr->key);
	}
	printf("\n");
}

删除数据

void Freenode(BstNode* ptr) {
	free(ptr);
}
bool Remove(BSTree* ptree, const KeyType kx) {
	assert(ptree!=nullptr);
	if (ptree->root == nullptr)return false;
	BstNode *ptr = FindValue(ptree,kx);
	if (ptr == nullptr) return false;
	//双分支 狸猫换太子
	if (ptr->leftchild != nullptr && ptr->rightchild != nullptr) {
		BstNode* nextnode = Next(ptr);
		ptr->key = nextnode->key;
		ptr = nextnode;
	}
	//叶子结点的删除  单分支
	BstNode* child = ptr->leftchild != nullptr ? ptr->leftchild : ptr->rightchild;//找出左右孩子中不为null的树
	BstNode* pa = ptr->parent;//找出双亲结点
	if (child != nullptr) child->parent = pa;//孩子不为null,直接将孩子的双亲指针指向待删除指针的双亲结点。
	if (pa != nullptr) {//如果待删除结点的双亲结点不为null,要将待删除结点的左右孩子指向待删除结点的孩子
		if (pa->leftchild == ptr) {
			pa->leftchild = child;
		}
		else {
			pa->rightchild = child;
		}
	}
	else {//根节点
		ptree->root = child;
	}
	Freenode(ptr);
	ptree->cursize -= 1;
	return true;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

*闲鱼

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值