[C语言]二叉搜索树

结构体

这个和普通的二叉树一样的

typedef struct BiTNode
{
	DataType data;    //数值 
	struct BiTNode* lchild;  //左孩子 
	struct BiTNode* rchild;  //右孩子 
	int flag;  //非递归遍历时可能会使用到
} BiTNode,*BiTree; //搜索树结构体 

二叉搜索树的插入(创建)

这个相当于创建了一棵二叉搜索树
我使用了递归和迭代两种方法,迭代效率更高些,不过更臃肿些

//递归插入并返回头节点
BiTree Insert_D(BiTree root, DataType data)
{
	if(!root)//找到了对应位置 那就插入(就是让这个空节点变成插入的这个节点)! 
	{
		root = (BiTree)malloc(sizeof(BiTNode));
		root->data = data;
		root->flag = 0;
		root->lchild = root->rchild = NULL;
	}
	else//没找到就继续找 
	{
		if(data < root->data)
			root->lchild = Insert_D(root->lchild,data);//往后面找 
		else
			root->rchild = Insert_D(root->rchild,data);
	}
	return root;//返回这一层的头节点指针 
}

//迭代插入并返回头节点
BiTree Insert(BiTree root, DataType data)
{
	BiTree Faceroot,Preroot;  //保存头节点
	Faceroot = root; //指向前一个节点
	while(root)//找到对应位置
	{
		Preroot = root;
		if(data < root->data)
			root = root->lchild;
		else
			root = root->rchild;
	}
	
	root = (BiTree)malloc(sizeof(BiTNode));//给要插入的数值创建空间 
	root->data = data;
	root->flag = 0;
	root->lchild = root->rchild = NULL;
	
	if(!Faceroot)   //用于给空树插入时返回第一个插入的节点的指针 
		return root;
	if(data < Preroot->data)//给Preroot后面插入新创建节点 
		Preroot->lchild = root;
	else
		Preroot->rchild = root;
	return Faceroot; //返回树的头节点指针
}

查找值

这里也是使用了递归和迭代,个人感觉这个的迭代更好写一些

//尾递归查找
//从root节点开始往下查找 关键值key,如果找到就返回key节点的指针,否则返回NULL
BiTree Find_D(BiTree root,DataType key)
{
	if(!root)
		return NULL;
	if(key < root->data)
		return Find_D(root->lchild,key);
	else if(key > root->data)
		return Find_D(root->rchild,key);
	else
		return root;
}

//迭代查找
//从root节点开始往下查找 关键值key,如果找到就返回key节点的指针,否则返回NULL
BiTree Find(BiTree root,DataType key)
{
	while(root)
	{
		if(root->data == key)
			break;
		else if(key < root->data)
			root = root->lchild;
		else
			root = root->rchild;
	}
	return root;
}

查找最小值和最大值

这里也是两种方法,差别也不大

//从root节点开始迭代查找最小值,找到就返回其指向该节点指针,否则返回NULL
BiTree FindMin(BiTree root)//最左边
{
	if(root)
		while(root->lchild)
			root=root->lchild;
	return root;
}

//从root节点开始迭代查找最大值,找到就返回其指向该节点指针,否则返回NULL
BiTree FindMax(BiTree root)//最右边 
{
	if(root)
		while(root->rchild)
			root=root->rchild;
	return root;
}

//递归查找最大值
BiTree FindMax_D(BiTree root)
{
	if(!root)
		return NULL;
	if(!root->rchild)
		return root;
	return FindMax_D(root->rchild);
}

删除单个节点

这个就需要慢慢来理解一下了(手绘了一下,有亿点点丑…)

删除节点大概三种方案
第一种:
在这里插入图片描述
第二种:
在这里插入图片描述
第三种:
在这里插入图片描述

//删除节点
BiTree Del(BiTree root, DataType key)
{
	if(root)
	{
		if(key < root->data)
			root->lchild = Del(root->lchild,key);
		else if(key > root->data)
			root->rchild = Del(root->rchild,key);
		else  //找到要删除的点啦 
		{
			if(root->lchild&&root->rchild)//两个孩子都存在的情况
			{
				BiTree temp = FindMin(root->rchild);  //寻找右孩子里的最小值 (ps:也可以左孩子里最大值) 然后让需要被删除的点的值变成那个最小值(或者最大值),然后去删除那个最小值...递归 
				root->data = temp->data;
				root->rchild = Del(root->rchild,root->data);
			}
			else //叶子节点或者只有一个孩子的情况 
			{
				BiTree temp = root;
				if(root->lchild) 	//有左孩子就让它等于左孩子节点
					root = root->lchild;
				else if(root->rchild)	//有右孩子就让它等于右孩子节点
					root = root->rchild;
				else	//如果是叶子节点就让它为空
					root = NULL;
				free(temp);
			}
		}
	}
	else
		printf("没有该节点\n");
	return root;//返回节点
}

遍历

//先序遍历 
void ProPrint(BiTree root)//这个的先序遍历和最开始输入的顺序是一样的,其他遍历方式可以参考之前写的文章
{
	if(!root)
		return;
	printf("%d ",root->data);
	ProPrint(root->lchild);
	ProPrint(root->rchild);
}

全部代码

注意:二叉搜索树的中序遍历是有序的

#include<stdio.h>
#include<stdlib.h>
typedef int DataType;  //自定义节点数值类型 

typedef struct BiTNode
{
	DataType data;    //数值 
	struct BiTNode* lchild;  //左孩子 
	struct BiTNode* rchild;  //右孩子 
	int flag;
} BiTNode,*BiTree;//搜索树结构体 

//尾递归查找
//从root节点开始往下查找 关键值key,如果找到就返回key节点的指针,否则返回NULL
BiTree Find_D(BiTree root,DataType key)
{
	if(!root)
		return NULL;
	if(key < root->data)
		return Find_D(root->lchild,key);
	else if(key > root->data)
		return Find_D(root->rchild,key);
	else
		return root;
}

//迭代查找
//从root节点开始往下查找 关键值key,如果找到就返回key节点的指针,否则返回NULL
BiTree Find(BiTree root,DataType key)
{
	while(root)
	{
		if(root->data == key)
			break;
		else if(key < root->data)
			root = root->lchild;
		else
			root = root->rchild;
	}
	return root;
}

//从root节点开始迭代查找最小值,找到就返回其指向该节点指针,否则返回NULL
BiTree FindMin(BiTree root)//最左边
{
	if(root)
		while(root->lchild)
			root=root->lchild;
	return root;
}

//从root节点开始迭代查找最大值,找到就返回其指向该节点指针,否则返回NULL
BiTree FindMax(BiTree root)//最右边 
{
	if(root)
		while(root->rchild)
			root=root->rchild;
	return root;
}

//递归查找最大值
BiTree FindMax_D(BiTree root)
{
	if(!root)
		return NULL;
	if(!root->rchild)
		return root;
	return FindMax_D(root->rchild);
}

//递归插入并返回头节点
BiTree Insert_D(BiTree root, DataType data)
{
	if(!root)//找到了对应位置 那就插入(就是让这个空节点变成插入的这个节点)! 
	{
		root = (BiTree)malloc(sizeof(BiTNode));
		root->data = data;
		root->flag = 0;
		root->lchild = root->rchild = NULL;
	}
	else//没找到就继续找 
	{
		if(data < root->data)
			root->lchild = Insert_D(root->lchild,data);//往后面找 
		else
			root->rchild = Insert_D(root->rchild,data);
	}
	return root;//返回这一层的头节点指针 
}

//迭代插入并返回头节点
BiTree Insert(BiTree root, DataType data)
{
	BiTree Faceroot,Preroot;  //保存头节点
	Faceroot = root; //指向前一个节点
	while(root)//找到对应位置
	{
		Preroot = root;
		if(data < root->data)
			root = root->lchild;
		else
			root = root->rchild;
	}
	
	root = (BiTree)malloc(sizeof(BiTNode));//给要插入的数值创建空间 
	root->data = data;
	root->flag = 0;
	root->lchild = root->rchild = NULL;
	
	if(!Faceroot)   //用于给空树插入时返回第一个插入的节点的指针 
		return root;
	if(data < Preroot->data)//给Preroot后面插入新创建节点 
		Preroot->lchild = root;
	else
		Preroot->rchild = root;
	return Faceroot; //返回树的头节点指针
}

//删除节点
BiTree Del(BiTree root, DataType key)
{
	if(root)
	{
		if(key < root->data)
			root->lchild = Del(root->lchild,key);
		else if(key > root->data)
			root->rchild = Del(root->rchild,key);
		else  //找到要删除的点啦 
		{
			if(root->lchild&&root->rchild)//两个孩子都存在的情况
			{
				BiTree temp = FindMin(root->rchild);  //寻找右孩子里的最小值 (ps:也可以左孩子里最大值) 然后让需要被删除的点的值变成那个最小值(或者最大值),然后去删除那个最小值...递归 
				root->data = temp->data;
				root->rchild = Del(root->rchild,root->data);
			}
			else //叶子节点或者只有一个孩子的情况 
			{
				BiTree temp = root;
				if(root->lchild) 	//有左孩子就让它等于左孩子节点
					root = root->lchild;
				else if(root->rchild)	//有右孩子就让它等于右孩子节点
					root = root->rchild;
				else	//如果是叶子节点就让它为空
					root = NULL;
				free(temp);
			}
		}
	}
	else
		printf("没有该节点\n");
	return root;//返回节点
}

//先序遍历 
void ProPrint(BiTree root)
{
	if(!root)
		return;
	printf("%d ",root->data);
	ProPrint(root->lchild);
	ProPrint(root->rchild);
}

int main()
{
	BiTree root = NULL;
	printf("请输入数值来建立二叉搜索树,q停止输入:");
	DataType data;
	while(scanf("%d",&data)==1)
		root = Insert(root,data);
	printf("二叉搜索树的先序遍历为:");
	ProPrint(root);
	putchar('\n');
	BiTree p;
	p = FindMax(root);
	if(p)
		printf("最大值为:%d\n",p->data);
	p = FindMin(root);
	if(p)
		printf("最小值为:%d\n",p->data);
	fflush(stdin);
	printf("请输入需要删除的节点的值:");
	scanf("%d",&data);
	Del(root,data);
	printf("二叉搜索树的先序遍历为:");//这个和输入的顺序是一样的... 
	ProPrint(root);
	return 0;
}

效果图

在这里插入图片描述
可能会有一些地方写的不好或者有错误,如果有建议请您指出,我一定洗耳恭听,谢谢

<think>嗯,用户让我详细介绍一下C语言中的二叉搜索树。首先,我得确认用户的基础知识水平。可能他们已经学过数据结构的基础,比如链表或者二叉树,但也许对二叉搜索树的具体实现不太清楚。需要从基本概念讲起,再逐步深入到操作和代码实现。 二叉搜索树的定义是关键,必须明确每个节点的左子树都小于根节点,右子树都大于根节点。这个特性决定了它的高效查找、插入和删除操作。接下来需要介绍结构体的定义,比如节点包含的数据和左右指针。 然后,操作部分要分点讲解。查找操作相对简单,递归和迭代两种方法都可以。插入操作需要考虑树为空的情况,以及如何递归地找到正确的位置。删除操作比较复杂,需要分三种情况处理:无子节点、有一个子节点、有两个子节点。特别是第三种情况,需要找到前驱或后继节点来替换,这部分容易出错,得详细说明。 代码示例是必要的,用户可能需要实际的例子来理解理论。每个函数的实现,比如search、insert、delete_node,都要解释清楚。特别是删除函数中的三种情况处理,要用代码展示如何操作指针,避免内存泄漏。 最后,应用场景和优缺点能帮助用户理解二叉搜索树的实际用途,比如数据库索引或文件系统。同时,要提到平衡二叉树的重要性,因为不平衡会导致性能下降,这样用户知道后续学习的方向。 检查一下是否有遗漏的部分,比如遍历方式或者常见错误。可能用户还想知道如何测试这些函数,或者如何处理重复的值,但问题中没有提到,是否需要补充?不过根据问题描述,用户可能只需要基础详解,所以保持核心内容即可。确保解释清晰,代码正确,逻辑连贯。</think>### 二叉搜索树(Binary Search Tree, BST)详解 #### 一、基本概念 **二叉搜索树**是一种特殊的二叉树数据结构,满足以下性质: 1. **左子树**所有节点的值 < 根节点的值 2. **右子树**所有节点的值 > 根节点的值 3. 左右子树也必须是二叉搜索树 这一特性使得BST在查找、插入和删除操作时具有高效性,平均时间复杂度为 **O(log n)**(平衡状态下)。 --- #### 二、C语言实现基础 **1. 节点结构定义** ```c typedef struct TreeNode { int data; struct TreeNode* left; struct TreeNode* right; } TreeNode; ``` **2. 核心操作** - **查找节点** ```c TreeNode* search(TreeNode* root, int key) { if (root == NULL || root->data == key) return root; return (key < root->data) ? search(root->left, key) : search(root->right, key); } ``` - **插入节点** ```c TreeNode* insert(TreeNode* root, int val) { if (root == NULL) { TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode)); newNode->data = val; newNode->left = newNode->right = NULL; return newNode; } if (val < root->data) root->left = insert(root->left, val); else if (val > root->data) root->right = insert(root->right, val); return root; // 忽略重复值 } ``` - **删除节点**(最复杂操作) ```c TreeNode* deleteNode(TreeNode* root, int key) { if (!root) return NULL; if (key < root->data) { root->left = deleteNode(root->left, key); } else if (key > root->data) { root->right = deleteNode(root->right, key); } else { // Case 1/2: 0-1个子节点 if (!root->left) { TreeNode* temp = root->right; free(root); return temp; } else if (!root->right) { TreeNode* temp = root->left; free(root); return temp; } // Case 3: 两个子节点,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值