二叉搜索树的相关笔记

二叉搜索树

1 二叉搜索树的概念

1.1 二叉搜索树(二叉排序树 二叉查找树)
根节点值大于其左子树中的所有节点的值
根节点值小于其右子树中的所有节点的值
左右子树都是二叉搜索树
1.2 二叉搜索树的存储结构

typedef * KeyType;
typedef struct 
{
	//用来比较结点的大小
	KeyType key;
}ElemType;
//这是二叉树的结点定义
typedef struct BiTNode
{
	ElemType data;
	struct BiTNode *lTree, *rTree;
}BiTNode, *BiTree;
//实际上 二叉搜索树本质上 还是 二叉树
//二叉搜索树 只是 在存储顺序上有特殊约定
typedef BiTNode BSTNode;
typedef BiTree BSTree;

2 二叉搜索树的查找
2.1
从根节点开始查找
如果是空树,则查找失败
若tgt 等于根节点的值, 查找成功
若tgt 小于根节点, 那么 目标可能在左子树上, 所以 递归查找左子树
若tgt 大于根节点, 那么 目标可能在右子树上, 所以 递归查找右子树

(如果不存在这个元素的话,最终会 到达叶子结点的子树, 也就是 null)
(这个操作对空树也满足)
代码实现
	//查找的元素结点,又不是树,
	//所以我选择返回结点,但是ppt上返回的是树
	//根据key值来查找(定位)
	BSTNode * BSTSearch(BSTree T, KeyType key)
	{
		if (T == NULL) return NULL;
		else 
		{
			if (T->data.key == key) 
				return T;
			else if (key < T->data.key)
				return BSTSearch(T->lTree, key);
			else if (key > T->data.key)
				return BSTSearch(T->rTree, key);
		}
	}
	//非递归实现
	/*
		p最初指向二叉搜索树的根节点, 只要p不为空且不是目标结点
		则往下查找
		如此重复直到 p为空 或者 找到
	*/
	BiTNode * BSTSearch(BSTree T, KeyType key)
	{
		BiTNode *p = T;
		while (p && p->data.key != key)
		{
			if (key < T->data.key)
				p = T->lTree;
			else if (key > T->data.key)
				p = T->rTree;
		}
		//if (p == NULL) return NULL;
		//else return p;
		//上面注释的两行可以写成一行
		return p;
	}

3 二叉搜索树的增删
3.1 插入节点的过程
先查找是否存在该节点,若不存在, 插入位置在最后走到的结点
的左右孩子位置插入

Status BSTInsert(BSTree &T, ElemType e)
{
	if (T == NULL)
	{
		BiTNode *p = (BiTNode*)malloc(sizeof(BiTNode));
		if (p == NULL) exit(OVERFLOW);
		p->data = e;
		p->lTree = NULL;
		p->rTree = NULL;
		/*
			为什么让 T = p呢?
			1. 若 原来就是个空树, 那么 这相当于给空树添加了个根节点
			2. 若遍历到最后一个结点, 此时 T 是 最后一个结点的 左右子树的指针
			指针存放 左右结点的 地址
		*/
		T = p;
	}
	else 
	{
		if (e.key == T->data.key)
			return ERROR;
		else if (e.key < T->data.key)
			BSTInsert(T->lTree, e);
		else 
			BSTInsert(T->rTree, e);
	}
}

3.2 二叉搜索树的创建

Status CreateBST(BSTree &T)
{
	T = NULL;
	InputElem(e);
	while (! IsEnd(e))
	{
		//反正我感觉 就算输入有重复 也不能直接退出
		if (BSTInsert(T, e) == ERROR)
			return ERROR;
		//这个 if else关系 没有那么强 
		else 
			InputElem(e);
	}
}

3.3 删除结点过程
删除叶子结点: 直接删除
删除只有一个子树的结点,直接将紫薯接到被删除结点的双亲指针上
(补充原结点的位置)
删除有两个子树的结点: 将左子树最大或右子树最小移至 删除位置

定位被删除结点及其 双亲结点, 以便修改在双亲结点上修改孩子的信息
void DeleteBST(BSTree &T, KeyType key)
{
	p = T;
	parent_p = NULL;
	//不能直接调用 BSTSearch, 因为我这个定位过程
	//涉及到 parent_p 的操作
	while (p != NULL && key != p->data.key)
	{
		if (key < T->data.key)
		{
			f = p;
			p = p->lTree;
		}
		else 
		{
			f = p;
			p = p->rTree;
		}
	}
	//若没有这个结点, 可以直接退出函数
	//相当于已经删除
	if (p == NULL) return ;
	
	//1 若删除的结点 没有孩子:
	//释放完结点p后, 还需要处理双亲的孩子指针
	//为什么要处理呢?
	//为了以后插入结点不会出错
	//否则 若双亲结点存储了垃圾地址
	//会被误认为存储了结点
	//在对 双亲指针处理时:
	//若删除的是 T的根节点(即parent_p为空, 释放完结点后,T需要赋空)
	if (p->lTree == NULL && p->rTree == NULL)
	{
		if (f == NULL)
		{
			free(p);
			p = NULL;
			f = NULL;
		}
		else if (f->lTree == p)
		{
			free(p);
			p = NULL;
			f->lTree = NULL;
		}
		else if (f->rTree == p)
		{
			free(p);
			p = NULL;
			f->rTree = NULL;
		}
	}
	
	//删除单子树的结点: 释放结点,双亲指针指向被删结点的子树
	//若双亲为空时:除释放结点外, T也需要赋空
	//下面只写了 右子树为空, 左子树不空的情况
	//左子树空, 右子树不空的代码同理
	if (p->rTree == NULL)
	{
		if (f == NULL)
		{
			T = p->rTree;
			free(p);
			p = NULL;
		}
		else if (f->lTree == p) 
		{
			f->lTree = p->rTree;
			free(p);
			p = NULL;
		}
		else if (f->rTree == p)
		{
			f->rTree = p->rTree;
			free(p);
			p = NULL;
		}
	}
	
	//删除双子节点: 找左子树最大结点(或右子树最小结点),其值赋予被删结点
	//释放真正被删结点
	//需要定位真正被删除结点及其双亲
	if (p->lTree && p->rTree)
	{
		BiTNode *q = NULL, *parent_q = NULL;
		parent_q = p;
		q = p->lTree;
		while (q->rTree)
		{
			parent_q = q;
			q = q->rTree;
		}
		//覆盖
		p->data = q->data;
		//删除左子树最大结点
		//被删结点一定有双亲,
		//被删结点不一定没有孩子,若果有 那么只有 左子树
		
		//特别注意这里的if else判断的含义
		if (parent_q->rTree == q)
			f->rTree = q->lTree;
		else 
			f->lTree = q->lTree;
		free(q);
	}		
}	

4 总结与推广

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值