数据结构实验9----二叉排序树

实验题目

实验题目: 二叉排序树任意给定一组数据,设计一个算法,建立一棵二叉排序树,对它进行查找、插入、删除操作。

实验说明:二叉排序树存储结构如下:

        二叉排序树插入算法伪代码如下: 

         二叉排序树中删除一个结点f的左孩子结点p算法伪代码如下:

实验内容

1.问题设计分析

        插入:在二叉排序树bt中插入一个关键字为k的结点,要保证插入后仍满足BST性质。其插入过程是:若bt为空,则创建一个key域为k的结点bt,将它作为根结点;否则将k和根结点的关键字比较,若k<bt->key,则将k插入bt结点的左子树中,若k>bt->key,则将k插入bt结点的右子树中,其他情况是k=bt->key,说明树中已有此关键字k,无须插入,最后返回插入后的二叉排序树的根结点bt。

        创建:创建一棵二叉排序树是从一个空树开始,每插入一个关键字,就调用一次插入算法将它插入当前已生成的二叉排序树中。

        查找:因为二叉排序树可看作有序的,所以在二叉排序树上进行查找和折半查找类似,也是一个逐步缩小查找范围的过程。递归查找算法SearchBST如下(在二叉排序树bt上查找关键字为k的结点,成功时返回该结点的地址,否则返回NULL)

        删除:从二叉排序树中删除一个结点时,不能直接把以该结点为根的子树都删除,只能删除该结点本身,并且还要保证删除后所得的二叉树仍然满足BST性质。也就是说,在二叉排序树中删除一个结点就相当于删除有序序列中的一个结点。删除操作必须首先进行查找,假设在查找结束时p指向要删除的结点。删除过程分为以下几种情况:

                (1)若p结点是叶子结点,直接删除该结点。

                (2)若p结点只有左子树而无右子树,根据二叉排序树的特点,可以直接用其左孩子替代结点p。

                (3)若p结点只有右子树而无左子树。根据二叉排序树的特点,可以直接用其右孩子替代结点p。

                (4)若p结点同时存在左、右子树。根据二叉排序树的特点,可以从其左子树中选择关键字最大的结点q,用结点q的值替代结点p的值,并删除结点q,其原理是用中序前驱替代被删结点。

2.程序编码

#include<stdio.h>
#include<malloc.h>
#define MaxSize 100
typedef int KeyType;
typedef char InfoType;
typedef struct node
{
	KeyType key;
	InfoType data;
	struct node *lchild,*rchild;
} BSTNode;
void DispBST(BSTNode *b);
bool InsertBST(BSTNode *&bt,KeyType k)
{
	if(bt==NULL)
	{
		bt=(BSTNode *)malloc(sizeof(BSTNode));
		bt->key=k;
		bt->lchild=bt->rchild=NULL;
		return true;
	}
	else if(k==bt->key)
		return false;
	else if(k<bt->key)
		return InsertBST(bt->lchild,k);
	else
		return InsertBST(bt->rchild,k);
}
BSTNode *CreateBST(KeyType A[],int n)
{
	BSTNode *bt=NULL;
	int i=0;
	while(i<n)
	{
		if(InsertBST(bt,A[i])==1)
		{
			printf("	第%d步,插入%d:",i+1,A[i]);
			DispBST(bt);
			printf("\n");
			i++;
		}
	}
	return bt;
}
int SearchBST(BSTNode *bt,KeyType k)
{
	if(bt==NULL)
		return 0;
	else if(k==bt->key)
	{
		printf("%3d",bt->key);
		return 1;
	}
	else if(k<bt->key)
		SearchBST(bt->lchild,k);
	else 
		SearchBST(bt->rchild,k);
	printf("%3d",bt->key);
} 
void DispBST(BSTNode *bt)
{
	if(bt!=NULL)
	{
		printf("%d",bt->key);
		if(bt->lchild!=NULL||bt->rchild!=NULL)
		{
			printf("(");
			DispBST(bt->lchild);
			if(bt->rchild!=NULL)
			printf(",");
			DispBST(bt->rchild);
			printf(")");
		}
	}
}
void Delete1(BSTNode *p,BSTNode *&r)
{
	BSTNode *q;
	if(r->rchild!=NULL)
	{
		Delete1(p,r->rchild);
	}
	else
	{
		p->key=r->key;
		p->data=r->data;
		q=r;
		r=r->lchild;
		free(q);
	}
}
void Delete(BSTNode *&p)
{
	BSTNode *q;
	if(p->rchild==NULL)
	{
		q=p;
		p=p->lchild;
		free(p);
	}
	else if(p->lchild==NULL)
	{
		q=p;
		p=p->rchild;
		free(p);
	}
	else Delete1(p,p->lchild);
}
bool DeleteBST(BSTNode *&bt,KeyType k)
{
	if(bt==NULL)
		return false;
	else
	{
		if(k<bt->key)
			return DeleteBST(bt->lchild,k);
		else if(k>bt->key)
			return DeleteBST(bt->rchild,k);
		else 
		{
			Delete(bt);
			return true;
		}
	}
}
int main()
{
	BSTNode *bt;
	int path[MaxSize];
	KeyType k=6;
	int a[]={4,9,0,1,8,6,3,5,2,7},n=10;
	printf("创建一颗BST树:");
	printf("\n");
	bt=CreateBST(a,n);
	printf("BST:");
	DispBST(bt);
	printf("\n");
	printf("查找%d关键字(逆序):",k);
	SearchBST(bt,k);
	printf("\n");
	printf("删除结点4:");
	DeleteBST(bt,4);
	DispBST(bt);
	printf("\n"); 
}

3.运行结果和分析

        结果如图所示:

4.实验小结

        插入:由于二叉排序树中的每个结点恰好存放一个关键字,所以插入关键字k就是插入一个结点。从插入算法InsertBST看到,每个结点插入时都需要从根结点开始比较,若比根结点的key值小,当前指针移到左子树,否则当前指针移到右子树,如此这样,直到当前指针为空,再创建一个存放关键字k的结点并链接起来。因此可知,任何结点插入二叉排序树时都是作为叶子结点插入的。

        创建:一个关键字集合有多个关键字序列,不同的关键字序列采用上述创建算法得到的二叉排序树可能不同。

        查找:和折半查找的判定树类似,将二叉排序树中的结点作为内部结点,可以添加相应的外部结点,具有n个内部结点的二叉排序树,其外部结点的个数为n+1。显然,在二叉排序树中进行查找,若查找成功,则是走了一条从根结点到某个内部结点的路径;若查找不成功,则是走了一条从根结点到某个外部结点的路径。因此与折半查找类似,其关键字比较的次数不超过树的高度。

        删除:除上面意外,还可以从其右子树中选择关键字最小的结点g,用结点q的值替代结点p的值,其原理是用中序后继替代被删结点。

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二叉排序树(Binary Search Tree,简称BST)是一种特殊的二叉树,它具有以下性质: 1. 左子树上的所有节点的值都小于根节点的值。 2. 右子树上的所有节点的值都大于根节点的值。 3. 左右子树也分别为二叉排序树二叉排序树主要用于实现动态查找,可以高效地插入、删除和查找元素。下面是二叉排序树的实现步骤: 1. 建立二叉排序树: - 如果树为空,将新节点作为根节点。 - 如果树不为空,从根节点开始,比较新节点的值与当前节点的值的大小关系: - 如果新节点的值小于当前节点的值,将新节点插入到当前节点的左子树中。 - 如果新节点的值大于当前节点的值,将新节点插入到当前节点的右子树中。 - 如果新节点的值等于当前节点的值,不进行插入操作。 2. 遍历二叉排序树: - 前序遍历:先访问根节点,然后递归地遍历左子树和右子树。 - 中序遍历:先递归地遍历左子树,然后访问根节点,最后递归地遍历右子树。 - 后序遍历:先递归地遍历左子树和右子树,最后访问根节点。 3. 删除节点: - 如果要删除的节点是叶子节点,直接删除即可。 - 如果要删除的节点只有一个子节点,将子节点替换为要删除的节点。 - 如果要删除的节点有两个子节点,可以选择用其前驱节点或后继节点替换。 4. 查找并记录访问次数: - 从根节点开始,比较要查找的值与当前节点的值的大小关系: - 如果要查找的值小于当前节点的值,继续在左子树中查找。 - 如果要查找的值大于当前节点的值,继续在右子树中查找。 - 如果要查找的值等于当前节点的值,找到了目标节点,并记录访问次数。 5. 利用快速排序的思想将负数排在正数前: - 在建立二叉排序树时,可以将负数插入到左子树中,将正数插入到右子树中,这样就可以实现负数排在正数前的效果。 以下是一个二叉排序树的示例代码: ```cpp #include <iostream> struct TreeNode { int value; TreeNode* left; TreeNode* right; }; void insert(TreeNode*& root, int value) { if (root == nullptr) { root = new TreeNode; root->value = value; root->left = nullptr; root->right = nullptr; } else if (value < root->value) { insert(root->left, value); } else if (value > root->value) { insert(root->right, value); } } void inorderTraversal(TreeNode* root) { if (root != nullptr) { inorderTraversal(root->left); std::cout << root->value << " "; inorderTraversal(root->right); } } int main() { TreeNode* root = nullptr; // 插入节点 insert(root, 5); insert(root, 3); insert(root, 7); insert(root, 2); insert(root, 4); insert(root, 6); insert(root, 8); // 中序遍历 inorderTraversal(root); // 输出:2 3 4 5 6 7 8 return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值