数据结构实验四

三阶B-树应用

程序的功能:应用3阶B-树,对图书馆的图书进行简单管理。采用3阶B-树存储结构,以书号为关键字,完成图书的入库、出库和查询操作。程序从文本文件中读取图书的书号,在完成入库出库操作后退出系统时可以把书号再保存到文本文件中。

输入的形式和输入值的范围:书号为整型数据,输入大于0的正整数。

输出的形式:进行入库、出库、查询操作时会输出操作结果或查询结果,如“添加成功”。

测试数据:对原文件中已有的书号进行删除,原文件中没有的书号进行添加,再入库出库过程中进行查询操作以验证操作是否正确执行,退出系统后检查文本文件是否更新。

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <assert.h>
//文件以txt形式保存,保存书号,每个记录之间以换行分隔
#define MIN_T 2
#define MAX_T 4//B-3树最少以及最多储存的结点
#define BTREE_NODE_SIZE sizeof(BTreeNodedata)
#define BTREE_SIZE sizeof(BTreedata)
typedef struct BTreeNodedata BTreeNodedata;
typedef struct BTreeNodedata *BTreeNode;
typedef struct BTreedata BTreedata;
typedef struct BTreedata *BTree;
struct BTreeNodedata//B树结点结构体
{
 int n;	//关键字个数
 int leaf;//记录是否为叶子结点,1为叶子节点 0反之
 int key[MAX_T - 1];	//关键字
 BTreeNode child[MAX_T];	//子结点
};
struct BTreedata//B树结构体
{
 BTreeNode	root;	//B树的根结点
};
BTreeNode allocate_node()
{
	BTreeNode node = (BTreeNode) malloc (BTREE_NODE_SIZE);
	return node;
}//为新结点分配空间
void btree_create(BTree tree)
{
	BTreeNode r_node = allocate_node();
	(r_node)->n = 0;
	(r_node)->leaf = 1;
	(tree)->root = r_node;
}//生成一棵空树,关键字个数为0,且为叶子结点
//在以node为根结点的树中,寻找关键字位置 返回关键字所在结点,并将关键字位置保存在location
BTreeNode btree_maximum(BTreeNode node)
{
	BTreeNode newnode = node;
	if(newnode->n < 1)
	{
		printf("this is null tree\n");
		return NULL;
	}
	if(node->leaf)
		return newnode;
	else
		newnode = btree_maximum(node->child[node->n]);
	return newnode;
}//返回以node为根结点树的最大关键字的结点,关键字的位置肯定为该结点的n-1值
BTreeNode btree_minimum(BTreeNode node)
{
	BTreeNode newnode = node;
	if(newnode->n < 1)
	{
		printf("this is null tree\n");
		return NULL;
	}
	if(node->leaf)
		return newnode;
	else
		newnode = btree_minimum(node->child[0]);
	return newnode;
}//返回以node为根结点树的最小关键字的结点,关键字的位置肯定为0
void btree_search(BTreeNode node, int key)
{
	int j = 0;
	/*   * 遍历当前结点,寻找恰当的关键字,如果找到相等的关键字,返回结点并将关键字位置保存在location
     * 如果没找到相等结点,且该结点为叶子结点,则报错
     * 否则递归寻找
     */
	while(j < node->n && key > node->key[j])
		j++;
	if(j < node->n && key == node->key[j])
	{
		printf("该图书在库内\n");
	}
	else if(node->leaf)
	{
		printf("该书不存在\n");
	}
	else  btree_search(node->child[j], key);}
void btree_split_child(BTreeNode node, int location)
{
	/* 建立新的空结点 */
	BTreeNode newnode = allocate_node();
	BTreeNode childnode = node->child[location];
	int i = 0;
	/* 初始化空结点newnode,将子结点childnode的信息复制到新结点node中 */
	newnode->leaf = childnode->leaf;
	newnode->n = MIN_T - 1;
	/* 将子结点childnode后T-1个关键字复制到新结点中,并改变子结点的n值 */
	for(i = 0;i <= MIN_T - 2;i++)
		newnode->key[i] = childnode->key[i + MIN_T];
	childnode->n = MIN_T - 1;
	/* 如果子结点非叶子结点,则相应的将子结点的结点点复制到新结点中 */
	if(!childnode->leaf)
		for(i = 0;i <= MIN_T - 1;i++)
			newnode->child[i] = childnode->child[i + MIN_T];
	/* 将父结点对应的关键字以及子结点位置向后移动一位 */
	for(i = node->n;i > location;i--)
	{
		node->key[i] = node->key[i - 1];
		node->child[i+1] = node->child[i];
	}
	/* 为父结点增加新的关键字和子结点,并修改n值 */
	node->child[location + 1] = newnode;
	node->key[location] = childnode->key[MIN_T - 1];
	node->n = node->n + 1;
}//分裂父结点node中位置为location的子结点的满结点
void btree_insert_nonfull(BTreeNode node, int key)
{
	int i = node->n - 1;
	if(node->leaf)
	{
	/* 该结点为叶子结点时,找到对应位置,将关键字插入,并对结点node做出修改 */
		while(i >=0 && key < node->key[i])
		{
			node->key[i+1] = node->key[i];
			i--;
		}
		node->key[i+1] = key;
		node->n = node->n + 1;
	}
	else
	{/*非叶子结点时,查找对应子结点,判断其是否为满结点,是,则分裂,否递归插入 */
		while(i >=0 && key < node->key[i])
			i--;
	        i++;
		if(node->child[i]->n == MAX_T - 1)
		{
			btree_split_child(node, i);
			if(key > node->key[i])
				i++;	}
		btree_insert_nonfull(node->child[i], key);	}
}//对非满节点进行插入关键字
void btree_insert(BTree tree, int key)/ * 对整棵树进行插入关键字
 * 当树为有且只有一个关键字,且已满时,需要建立新的结点作为树的根结点,
 * 而当原树的根结点作为新结点的子结点,进行分裂操作
 * 否则,直接进行非满结点插入操作 */
{
	BTreeNode r_node = tree->root;
	if(r_node->n == MAX_T - 1)
	{
		BTreeNode r_node_new = allocate_node();
		r_node_new->leaf = 0;
		r_node_new->n = 0;
		r_node_new->child[0] = r_node;
		tree->root = r_node_new;
		btree_split_child(r_node_new, 0);
		btree_insert_nonfull(r_node_new, key);
	}
	else btree_insert_nonfull(r_node, key);
}
/* 当下降的结点node的关键字个数为T-1时,
 * 为了满足下降过程中,遇到的结点的关键字个数大于等于T,
 * 对结点parent、node、othernode三个结点的关键字做调整。
 * 当node在other左侧时,即node的右结点时(父结点的右子结点), * 在T+1位置,增加一个关键字,其值为父结点对应的关键字值,
 * 将父结点对应关键字值赋值为右子结点中的第一个关键字。
 * 将右子结点的关键字和子结点(如果有的话)向前移动一位
 * 修改右子结点以及该结点的n值*/
void btree_left(BTreeNode parent, BTreeNode node, BTreeNode othernode, int location)
{
	int i = 0;
	node->key[node->n] = parent->key[location];
	parent->key[location] = othernode->key[0];
	for(i = 0; i <= othernode->n - 2; i++)
		othernode->key[i] = othernode->key[i + 1];
	if(!othernode->leaf)
	{
		node->child[node->n + 1] = othernode->child[0];
		for(i = 0; i <= othernode->n - 1; i++)
			othernode->child[i] = othernode->child[i + 1];
	}
	node->n = node->n + 1;
	othernode->n = othernode->n - 1;
}
/* 当下降的结点node的关键字个数为T-1时,
 * 为了满足下降过程中,遇到的结点的关键字个数大于等于T,
 * 对结点parent、node、othernode三个结点的关键字做调整。
 * 当node在other右侧时,即node的左结点时(父结点的左子结点),
 * node结点的关键字和子结点(如果有的话)向后移动一位,
 * 在第一个位置增加一个关键字,其值为父结点对应的关键字值,
 * 将父结点对应关键字值赋值为左子结点中的最后一个关键字。
 * 修改左子结点和该结点的n值 */
void btree_right(BTreeNode parent, BTreeNode node, BTreeNode othernode, int location)
{
	int i = 0;
	for(i = node->n - 1; i >= 0; i--)
		othernode->key[i+1] = othernode->key[i];
	node->key[0] = parent->key[location];
	parent->key[location] = othernode->key[othernode->n];
	if(!node->leaf)
	{
		node->child[0] = othernode->child[othernode->n + 1];
		for(i = othernode->n; i >= 0; i--)
			othernode->child[i + 1] = othernode->child[i];}
	node->n = node->n + 1;
	othernode->n = othernode->n - 1;}
/*合并两个关键字个数为T-1父结点为parent位置为location的子结点
 * 以父结点对应的关键字为中间值连接两个子结点
 * 并返回需要下降的子结点位置 */
int btree_merge_child(BTreeNode parent, int location)
{
	int i;
	BTreeNode	lnode = NULL;
	BTreeNode	rnode = NULL;
	if(location == parent->n)
		location--;
	lnode = parent->child[location];
	rnode = parent->child[location + 1];
/* 将父结点对应的关键字以及右兄弟所有的关键字复制该结点,同时修改左子的n值 */
	lnode->key[lnode->n] = parent->key[location];
	for(i = 0; i < rnode->n; i++)
	{
		lnode->key[MIN_T + i] = rnode->key[i];
		lnode->n++;
	}
	/* 如果有子结点同样复制到该结点 */
	if(!rnode->leaf)
		for(i = 0; i <= rnode->n; i++)lnode->child[MIN_T + i] = rnode->child[i];
	rnode->n= 0;
	lnode->n = MAX_T - 1;
	/* 对父结点相应的关键字和子结点位置发生变化 */
	for(i = location; i < parent->n - 1; i++)
	{
		parent->key[i] = parent->key[i + 1];
		parent->child[i + 1] = parent->child[i + 2];}
	/* 调整父结点的n值 */
	parent->n = parent->n - 1;
	rnode = NULL;
	return location;}
/*对叶子结点node位置为location的关键字删除
  直接将位置location后的关键字向前移动一位*/
void btree_delete_leaf(BTreeNode node, int location)
{    int i = 0;
	for(i = location; i < node->n - 1; i++)
		node->key[i] = node->key[i + 1];
	node->n = node->n - 1;}
int btree_delete_node_in(BTreeNode r_node, int i)/*删除该层数组坐标为i的关键字*/
{
	BTreeNode lnode = r_node->child[i];
	BTreeNode rnode = r_node->child[i + 1];
	int temp = 0;
	/* 当前于该位置的关键字的左子结点关键字个数大于等于T时,
	 寻找该位置的关键的前驱(左子结点的最大关键字)*/
	if(lnode->n >= MIN_T)
	{
		BTreeNode newnode = btree_maximum(lnode);
		temp = r_node->key[i];
		r_node->key[i] = newnode->key[newnode->n - 1];
		newnode->key[newnode->n - 1] = temp;
	}
   //相反的,若右子结点符合条件,则找寻后继(即右子结点的最小关键字)
	else if(rnode->n >= MIN_T)
	{
		BTreeNode newnode = btree_minimum(rnode);
		temp = r_node->key[i];
		r_node->key[i] = newnode->key[0];
		newnode->key[0] = temp;
		i++;
	}
   //当左右子结点都不符合条件,则合并两个子结点
	else	i = btree_merge_child(r_node, i);
	return i;
}
// 删除以r_node为根结点的树的关键字key
void btree_delete_node(BTreeNode r_node, int key)
{
	int i = 0;
	/* 寻找关键字位置,或者下降的子结点位置 */
	while(i < r_node->n && key > r_node->key[i])
		i++;
/* 若再该层且为叶子结点删除结点,否则下降寻找结点删除 */
	if(i < r_node->n && key == r_node->key[i])
		if(r_node->leaf)
			{btree_delete_leaf(r_node, i);printf("删除成功\n");}
		else
		{
			i = btree_delete_node_in(r_node, i);
			btree_delete_node(r_node->child[i], key);
		}
	else
	{
		if(r_node->leaf)
			printf("该书不存在\n");
		else
		{
			if(r_node->child[i]->n >= MIN_T){
				btree_delete_node(r_node->child[i], key);}
			else
			{
				if(i > 0 && r_node->child[i - 1]->n >= MIN_T)
				{
					btree_right(r_node, r_node->child[i], r_node->child[i - 1], i);}
				else if(i < r_node->n && r_node->child[i + 1]->n >= MIN_T)
					btree_left(r_node, r_node->child[i], r_node->child[i + 1], i);
				else
					i = btree_merge_child(r_node, i);
				btree_delete_node(r_node->child[i], key);
			}
		}
	}
}
/*删除树内的关键字key,如果根结点为空,则替换根结点*/
void btree_delete(BTree tree, int key)
{
 BTreeNode r_node = tree->root;
 btree_delete_node(r_node, key);
 if(tree->root->n == 0 && tree->root->leaf == 0)
   tree->root = tree->root->child[0];
}
int main()
{
    BTree tree = (BTree) malloc (BTREE_SIZE);
	tree->root	= (BTreeNode) malloc (BTREE_NODE_SIZE);
	btree_create(tree);
	FILE* fp = fopen("library.txt", "r");
	if (fp == NULL) {
		printf("Failed to open file");
	}
	int i = 0,a[100];
	for(i=0;!feof(fp);i++)fscanf(fp,"%d\n",&a[i]);
	a[i]=0;
	fclose(fp);
	for(i = 0; a[i]!=0; i++){
		btree_insert(tree, a[i]);
		}
    system("color F0");
    while(1)
    {
        system("cls");//清空界面
			printf(" ***********************************************\n");
			printf(" ^_^  ^_^  ^_^  ^_^  ^_^  ^_^  ^_^  ^_^  ^_^ ^_^\n");
			printf(" ***********************************************\n");
			printf(" ______________________________________________\n");
			printf("┃                                            ┃\n");
			printf("┃               图书管理系统                 ┃\n");
			printf("┃____________________________________________┃\n");
			printf("***********************************************\n");
			printf("***********************************************\n");
			printf("**              1.新书入库                   **\n");
			printf("**              2.查询图书                   **\n");
			printf("**              3.图书出库                   **\n");
            printf("**              4.退出系统                   **\n");
			printf("***********************************************\n");
			printf("***********************************************\n");
			printf("^_^  ^_^  ^_^  ^_^  ^_^  ^_^  ^_^  ^_^  ^_^ ^_^\n");
			printf("***********************************************\n");
			printf("               选择操作:");
            int option,m,j;
            scanf("%d",&option);
           switch (option)
          {
           case 1:  {system("cls");
               printf("请输入你想添加的书号:");scanf("%d",&m);
               btree_insert(tree, m);
               a[i]=m;i++;a[i]=0;
               printf("添加成功\n");
               system("pause");break;} //增加图书
           case 2: {system("cls");
               printf("请输入你想查找的书号\n");scanf("%d",&m);
               btree_search(tree->root,m);
               system("pause");break;} //查找图书
           case 3: {system("cls");
               printf("请输入你想删除的书号\n");scanf("%d",&m);
               btree_delete(tree, m);
               for( j=0;a[j]!=m;j++){}
               a[j]=-1;
               system("pause");break;} //删除图书
           case 4:{printf("感谢您的使用");
           FILE* fp = fopen("library.txt", "w");
           for(i=0;a[i]!=0;i++)if(a[i]!=-1){fprintf(fp, "%d",a[i]);
		   fputc('\n', fp);}fclose(fp);exit(0);
           break;}//退出时保存文件
           default:break;
          }}
    return 0;}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值