数据结构——二叉搜索树

一、二叉搜索树

所有根节点大于左子树的节点,小于右子树的节点的二叉树。

性质:

  1. 如果左子树不为空,则左子树上的所有节点都小于根节点

  2. 如果右子树不为空,则右子树上的所有节点都大于根节点

3.左右子树也为二叉搜索树

二叉搜索树的算法时间复杂度的分析

二叉搜索树与序列元素的插入顺序有关,即元素按照不同的序列建立二叉搜索树会出现不同结构的树。

对于相对平衡的二叉搜索树(接近完全二叉树) ,操作的时间复杂度为 (n为节点个数,logn为树的层数)

在最坏情况下,二叉搜索树呈左单支(或右单支)树,此时层数与节点数相同,时间复杂度为

二叉搜索树主要实现 高效的数据查找和排序

1.查找:根据二叉搜索树的性质,通过比较节点的键值快速定位目标节点 ,对于一棵相对平衡的二叉搜索树,每次的查找时间复杂度通常为 ,n为节点的个数

2.排序:对于一个无序的序列,利用二叉搜索树的性质进行排序,将序列中的元素依次插入到二叉搜索树中,然后进行中序遍历 ,即可按升序输出序列。对于一个相对平衡的二叉搜索树,时间复杂度在 (n个元素,n次二叉树操作)

二、二叉搜索树的操作

首先用结构体定义树

复制代码
typedef struct tree{
	struct tree *left;
	struct tree *right;
	int data; 
}tree;

1. 树的建立(插入元素)

① 若树为空,直接插入

复制代码
tree *temp=root;
if(temp == NULL)
{
	root=(tree *)malloc(sizeof(tree));
	root->data=x;
	root->left=NULL;
	root->right=NULL;
	return ;
}

② 树不为空,按照二叉树搜索的性质插入位置,此时插入的该元素一定是叶子节点

有两种插入方法

非递归写法

复制代码
// 非递归写法
tree *tx=temp;
while(temp!=NULL)  // temp为空弹出,tx为temp的上一级 即将新节点插入到tx的下方
{
	tx=temp;   
	if(x<temp->data) temp=temp->left;
	else if(x>temp->data) temp=temp->right;
	else return ;
}
if(x<tx->data) //插入到左孩子
{
	tx->left=(tree*)malloc(sizeof(tree)); 
	tx->left->data=x;
	tx->left->left=NULL;
	tx->left->right=NULL;
}
else // 插入到右孩子
{
	tx->right=(tree*)malloc(sizeof(tree));
	tx->right->data=x;
	tx->right->left=NULL;
	tx->right->right=NULL;
}

递归写法

复制代码
void insert(tree **troot,int x) // 非递归写法 
{
	tree *temp=*troot;
	if(temp == NULL)
	{
		(*troot)=(tree*)malloc(sizeof(tree));
		(*troot)->data=x;
		(*troot)->left=NULL;
		(*troot)->right=NULL;
		return ;
	}
	if(x<temp->data) insert(&temp->left,x);
	else if(x>temp->data) insert(&temp->right,x);
	else return ;
}

插入过程图示:

③ 树不为空,若插入的数据在树中已存在,则不将数据插入到树中。二叉搜索树的数据都是独一无二的。

2.树的查询

树的查询就是运用二叉树的性质通过递归去查找元素。

复制代码
bool search(int x) // 查找 
{
	tree *temp=root;
	while(temp!=NULL)
	{
		if(x<temp->data) temp=temp->left;
		else if(x>temp->data) temp=temp->right;
		else return true;
	}
	return false;
}

3.树的删除节点操作

删除节点的操作与前两种复杂很多,需要考虑到许多种情况。

首先需要找到删除的节点,与树的查询类似,运用二叉树的性质查找,并且记录其父节点。

复制代码
tree *temp=root,*temp1=root; // temp为删除的节点 temp1为删除节点的父节点 
while(temp!=NULL && temp->data!=x)
{
	temp1=temp; // temp1储存为temp的前一个节点
	if(x<temp->data) temp=temp->left;
	else if(x>temp->data) temp=temp->right;
}

①当删除节点是叶子节点(判断条件:无左右子树)

直接删除即可,再将其父节点到该节点的指针清除

需要判断删除节点是父节点的左孩子还是右孩子

复制代码
if(temp->left==NULL && temp->right==NULL) // 删除的为叶子节点 
{
	if(temp1->left==temp) temp1->left=NULL; // 为左孩子,并将该处指针赋空
	else if(temp1->right==temp) temp1->right=NULL; // 为右孩子
}

②当删除节点只有一个孩子(只有左孩子或者右孩子)

以只有右孩子为例,左孩子同理。将删除节点的父节点直接指向其右孩子(这里仍需要判断删除节点时是父节点的左孩子还是右孩子),并释放删除节点, 赋为空(NULL)

复制代码
if(temp->right==NULL) //只有左孩子,右孩子为空 
{
	if(temp1->left->data == temp->data) // 删除节点为父节点的左孩子 
	{
		temp1->left=temp->left;
		tree *free_temp=temp;
		free(free_temp);
	    free_temp=NULL;
	}
	else  // 删除节点为父节点的右孩子 
	{
		temp1->right=temp->left;
		tree *free_temp=temp;
		free(free_temp);
		free_temp=NULL;
	}
}
else if(temp->left==NULL) // 只有右孩子,左孩子为空
{
	if(temp1->left->data==temp->data) // 删除节点为父节点的左孩子 
	{
		temp1->left=temp->right;
		tree *free_temp=temp;
		free(free_temp);
		free_temp=NULL;
	}
	else  // 删除节点为父节点的右孩子 
	{
		temp1->right=temp->right;
		tree *free_temp=temp;
		free(free_temp);
		free_temp=NULL;
	}
}

③当删除节点既有左孩子又有右孩子

有两种考虑方法:左子树的最大值节 点 或右子树的最小值节点替换删除节点

这种方法实现的原因:二叉搜索树删除节点的后,需要保证再次中序遍历时,仍然保持其原来的顺序,以找左子树最大替换图示。

找左子树最大的方法:就是找以删除节点的左孩子为根的树的最大值节点,同时记录其父节点

复制代码
tree *maxn=temp->left,*maxn1=temp->left; // maxn maxn1找左子树最大,其中maxn存储左子树最大的节点  maxn1 为其父节点 
// 找出的节点一定没有右节点 
while(maxn->right!=NULL) 
{
	maxn1=maxn;
	maxn=maxn->right;
}

过程顺序:将 maxn 的 data数据 赋到 删除节点 temp 上

①若删除节点不是 maxn 的父节点,直接将其父节点 maxn1 的右节点 赋为 maxn 的左节点,即 maxn 的左子树全部接到 maxn1 的右子树 上**( maxn只有左孩子 没有右孩子,否则maxn在查找时仍然回往下)**

复制代码
if(temp!=maxn1) // 如果删除节点不是左子树最大节点的父节点 
{
	maxn1->right=maxn->left;  // 删除节点的父节点 接上 删除节点的左子树 
	free(maxn);
	maxn=NULL;
}

②当删除节点是 maxn 的父节点 (那么,其父节点的左孩子一定是 maxn ),其他与上同理

复制代码
else
{
	maxn1->left=maxn->left;
	free(maxn);
	maxn=NULL;
}
相关推荐
算AI13 小时前
人工智能+牙科:临床应用中的几个问题
人工智能·算法
我不会编程55514 小时前
Python Cookbook-5.1 对字典排序
开发语言·数据结构·python
owde15 小时前
顺序容器 -list双向链表
数据结构·c++·链表·list
第404块砖头15 小时前
分享宝藏之List转Markdown
数据结构·list
hyshhhh15 小时前
【算法岗面试题】深度学习中如何防止过拟合?
网络·人工智能·深度学习·神经网络·算法·计算机视觉
蒙奇D索大15 小时前
【数据结构】第六章启航:图论入门——从零掌握有向图、无向图与简单图
c语言·数据结构·考研·改行学it
A旧城以西16 小时前
数据结构(JAVA)单向,双向链表
java·开发语言·数据结构·学习·链表·intellij-idea·idea
杉之16 小时前
选择排序笔记
java·算法·排序算法
烂蜻蜓16 小时前
C 语言中的递归:概念、应用与实例解析
c语言·数据结构·算法
OYangxf16 小时前
图论----拓扑排序
算法·图论