数据结构——二叉搜索树

一、二叉搜索树

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

性质:

  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;
}
相关推荐
xiaoshiguang33 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡3 小时前
【C语言】判断回文
c语言·学习·算法
别NULL3 小时前
机试题——疯长的草
数据结构·c++·算法
TT哇3 小时前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯
ZSYP-S4 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos4 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习4 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA5 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo5 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc5 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法