数据库系统实现实验报告实验名称: B -树索引的建立一、实验内容使用B-树对数据库进行索引。
按照学生ID建立起B树索引。
实验需要建立两个文本文件:数据文件datafile.txt和命令文件command.txt。
数据文件包含了所有需要建立检索的学生信息,文本中的每一行包含一个学生的信息。
每一行将由6个空格分隔的字符段组成:ID (9位),姓(最多15个字符),名(最多15个字符),年级(1位),专业(最多4个字符),以及邮箱地址(最多20个字符)。
二、实验分析:三、步骤分析及流程图步骤分析:一个具有10,000,000个记录的文本文件共计10,000,000*100B=1000MB,而内存只有50MB,50MB/4KB=50*1024 KB/4KB=12800块,每块可以存放4*1024B/100B=40个记录,每块剩余96KB,内存一共可以存放12800*40=512000个记录,一共有10,000,000个记录。
所以要进行10,000,000/512000=19.53次,即20次排序,每次排序的记录数为10,000,000/20=500,000个记录。
因此此次实验需要将文本文件分成20个子文件。
分别对子文件分别进行内部排序。
最后对20个排好序的子文件进行归并排序,完成排序。
故将其分为三个阶段1.生成一个具有10,000,000个记录的文本文件data.txt,其中每个记录由100个字节组成,其中只有一个整数类型属性A,剩余字节用0填充。
程序生成一个4个字节之内随机整数作为每条记录的属性,剩余字节用0填充。
记录写入data.txt文件中。
2.根据实验分析,将data.txt文件分为20个子文件,并且按照文件中每个记录的属性对各个子文件进行内部排序,最终形成20个有序的子文件data1.txt,data2,txt,…data20.txt。
3.对20个有序的子文件进行归并排序,最终形成一个有序的结果文件result.txt。
流程图:阶段一流程图见图1.1图1.1 随机生成10,000,000个记录的文件流程图阶段二流程图见图1.2图1.2 分离出20个内部有序的子文件流程图阶段三流程图见图1.3图1.3 将20个有序的子文件进行归并排序的流程图四、算法优化开始在阶段二,我们小组是利用自己写在类JL里的函数sort,采用选择序方法,运行后发现因为数据量大,产生速度特别慢。
后来尝试用了快速排序,但是上网查了之后,发现调用java里面原有的排序函数速度更快。
五、实验中遇到的问题及解决问题:函数produceResult()中我在写路径的时候,不小心多写了”/.txt”而这个是通过编译的,却导致阶段三长时间运行不出来,开始还以为算法出了问题。
解决:将路径正确书写。
六、实验结果及分析1.生成一个具有10,000,000个记录的文本文件xyj.txt。
2.将data.txt文件分为20个子文件,并且按照文件中每个记录的属性对各个子文件进行内部排序,最终形成20个有序的子文件data0.txt,data1.txt,…data19.txt。
这个阶段所用时间为46秒。
3.对20个有序的子文件进行归并排序,最终形成一个有序的结果文件result.txt。
所用时间为40秒。
4.按照教材cylinder-based buffers(1M bytes)的方法,修改第二阶段的算法所得结果:而为改进前的结果为可见,第二阶段排序所用时间为46秒,比上次时间的7分46秒明显缩短,跟我们预期的结果一致。
七、实验小结这次实验是我第一次和他人合作一起做一个程序,感觉沟通交流还是非常重要的,还有要花时间去理解他人的想法,这些都让我收获良多。
至于阶段三的归并排序,因为此前已经在数据结构中用C++实现过,即使不太擅长java语言,还是能较快的做出来。
通过这次实验,我了解了数据库中的大量数据的读取并没有想象中那么简单,在数据量多的时候,一个好的算法的作用是很大的,也更好的理解了归并排序在大文件排序中的作用和性能。
代码部分BTree.h文件#include<iostream>#include<string>#include<Queue>#include<List>#include<math.h>using namespace std;#define m 3 //B树的阶即一个节点有3个键值,4个指针//学生类class Student{public:string StudentId; //学生ID (9位)string StudentFirstName; //姓(最多15个字符)string StudentLastName; //名(最多15个字符)int grade; //年级(1位)string Major; //专业(最多4个字符)string email; //邮箱地址(最多20个字符)int Id; //int型的学生id,便于节点处理Student(){} //空的构造函数Student(string id, string firstn, string lastn, int grade, string major, string mail); //构造函数};//node类即B树中的一个节点class Node{public:int num; //一个节点对应的键值个数int key[m + 1]; //一个节点键值,之所以+1,便于后面节点分裂Node * ptr[m + 1]; //一个节点的指针数Node * parent; //指向父节点的指针Node(); //默认构造函数让所有值为空Node(int number, int k[]); //构造函数初始化一个节点,它的键值为数组k的值,键值个数为number个bool isLeafNode(Node * node);//判断是否为叶子节点};//Result结构体用在查询函数上struct Result{Node * nptr; //对应数据键值所在节点的指针int position; //对应数据键值在节点中的位置,从0开始算bool tag; //该键值是否在节点中};//B树类class BTree{public:Result search( int id); //查找学生id(键值)void insert1(int id); //添加数据键值函数1,即情况1:叶节点未满,插入数据后不用分裂节点void insert2(Result a, int id); //添加数据键值函数2,即情况2:叶节点满,插入数据后需要分裂叶节点,但是内层节点还有空间不需要分裂void insert3(Node * cur, Node * b); //添加数据键值函数3,即情况3:在情况2的基础上,内层节点已满,也需要分裂void deleteKey(int id); //删除数据的函数void printTree(); //输出树void initTree(); //默认的一颗树,这里用了ppt上的树BTree(); //默认的构造函数private:Node * root; //根节点};文件Btree.cpp#include<iostream>#include<string>#include"BTree.h"#include<cstdlib>#include<math.h>using namespace std;/*===================================================================================== ==================*//*Student类中成员函数的具体实现*//*===================================================================================== ==================*///构造函数,初始化Student对象Student::Student(string id, string firstn,string lastn, int grad, string major, string mail) {StudentId = id;StudentFirstName = firstn;StudentLastName = lastn;grade = grad;Major = major;email = mail;Id = atoi(id.c_str()); //string 转int,需要include cstdlib}/*===================================================================================== ==================*//*Node类中成员函数的具体实现*//*===================================================================================== ==================*///默认构造函数所有值设为0或空Node::Node(){num = 0;key[0] = NULL;key[1] = NULL;key[2] = NULL;key[3] = NULL;ptr[0] = NULL;ptr[1] = NULL;ptr[2] = NULL;ptr[3] = NULL;}//构造函数初始化一个节点,键值个数为number个,它的键值为数组k的值,Node::Node(int number, int *k){num = number;for (int i = 0; i < number; i++)key[i] = k[i];ptr[0] = NULL;ptr[1] = NULL;ptr[2] = NULL;ptr[3] = NULL;}//判断 node是不是叶子节点bool Node::isLeafNode(Node * node){Node* a = node->ptr[0];if (a == NULL) //当节点的最左指针已经没指向任何值时该节点为叶子return true;elsereturn false;}/*===================================================================================== ==================*//*BTree类中成员函数的具体实现*//*===================================================================================== ==================*///构造函数初始化树BTree::BTree(){initTree();//root = NULL;}//默认的一颗树,这里用了ppt上的树void BTree::initTree(){int n1[] = { 2, 3, 5 },n2[] = { 7, 11 },n3[] = { 13, 17, 19 },n4[] = { 23, 29 },n5[] = { 31, 37, 41 },n6[] = { 43, 47 },n7[] = { 7 },n8[] = { 23, 31, 43 },n9[] = { 13 };Node *leaf1 = new Node(3, n1);Node *leaf2 = new Node(2, n2);Node *leaf3 = new Node(3, n3);Node *leaf4 = new Node(2, n4);Node *leaf5 = new Node(3, n5);Node *leaf6 = new Node(2, n6);Node *parent1 = new Node(1, n7);Node *parent2 = new Node(3, n8);Node *Troot = new Node(1, n9);root = new Node(1, n9);root->ptr[0] = parent1;root->ptr[1] = parent2;parent1->ptr[0] = leaf1;parent1->ptr[1] = leaf2;parent2->ptr[0] = leaf3;parent2->ptr[1] = leaf4;parent2->ptr[2] = leaf5;parent2->ptr[3] = leaf6;leaf1->parent = parent1;leaf2->parent = parent1;leaf3->parent = parent2;leaf4->parent = parent2;leaf5->parent = parent2;leaf6->parent = parent2;parent1->parent = root;parent2->parent = root;}//查找学生id(键值)Result BTree::search(int id){Result r;if (root == NULL) //如果根节点为空直接返回false结果{r.position = 0;r. nptr = NULL;r.tag = false;return r;}else{Node * node = root;//从根节点开始查找向下搜索int i;while (!node->isLeafNode(node)){for (i = 0; i < node->num; i++)//遍历一个节点中有数据的键值{if (node->key[i] > id)//因为一个节点中的键值是从小到大排列的,当该键值大于id时,该id在该节点键值的对应的左部的指针指向的子树中{node = node->ptr[i];//到下一节点那查找break;}if (i == node->num - 1)//当遍历到最后一个节点还没有结束,说明是在最大键值的右边的指针指向的子树{node = node->ptr[i + 1];//到下一节点那查找break;}}}//在节点里寻找键值的位置for (i = 0; i < node->num; i++){if (node->key[i] == id)//找到该键值{r. nptr = node;r.position = i;//i为该键值在节点中的位置,注:i从0开始记录r.tag = true;return r;//找到键值返回该结果}else if (node->key[i] > id) //如果键值不存在跳出循环注:若没找到,i定位就在稍大于要查找的键值对应的键的位置break;}r. nptr = node;r.position = i;//注:没找到,特殊情况:当键值大于节点中键值最大的那个值,i定位在最后那一个有数据的键值对应的位置,i是从0开始的r.tag = false;//没找到,不存在该键值return r; //这里不存在的情况也要记录位置是为了下面的函数插入的时候知道应插入的位置}}/****************************************插入键值*************************************************************/void BTree::insert1(int id) //添加数据键值函数1,即情况1:叶节点未满,插入数据后不用分裂节点{Result re = search(id);if (re.tag){cout << "已存在该键值" << endl;return;}else{if (re . nptr ->num == m) //要添加到的节点键值满了调用需要分裂的添加注:这里可能用m-1更好,代表对应的阶,前面的阶改成4insert2(re , id);else{/*没满直接在当前节点里添加数据因为叶节点只有两种情况要嘛2个键要嘛3个键B树要求叶节点最后一个指针指向下一个叶节点,其他的n个指针至少要有[(n+1)/2](向下取整)个指针被使用,所以对应n=3(键值)的b树,至少要有2个指针被使用,即至少有两个键是要有数据的在此情况下,叶节点就两种情况,2个键值,或三个键值,这里只考虑了节点最多只有三个键值的情况*/Node * cur = re.nptr ; //获取当前要插入的节点,Result re =search(id); 前面没有找到时,已经记录对应的nodeint j;/*注:可以用插入10来举例,key[3]即最后额外增加的那个空间,用来顺移,要插入的位置是键值稍大于要插入键值的键值前面去,将节点中对应要插入的位置对应的值开始依次向后复制,再插入对应的值*/for (j = re.position; j<m; j++)cur->key[j + 1] = cur->key[j];cur->key[re.position] = id;//插入键值}}}//更具体的添加当节点需要分裂时的添加void BTree::insert2(Result re , int id){Node * cur = re.nptr ; //获取当前要插入的节点int j;for (j = 3; j - re.position>0; j--)//j=re.position 时停止,即要插入键值之后的键值赋值后移已经完成cur->key[j] = cur->key[j - 1];cur->key[re . position ] = id; //先插入键值此时用到了key数组预留的一位值int newkey[] = { cur->key[2], cur->key[3] };//取后面两个数据做成新节点int value = cur->key[2];// value是要插入上一层的键值cur->key[2] = NULL; cur->key[3] = NULL; //当前的后面两个数据清空cur->num = 2; //个数变化Node * newnode = new Node(2, newkey);//新加入的节点注:新节点为较大的那两个键值组成的newnode->parent = cur->parent; //指针变化Node * parent1 = cur->parent;//内层节点要求至少有(n+1)/2(向上取整)个指针被使用,这里的内层节点至少为两个指针,一个键值//这里插入分三种情况,父节点有1个键值,有2个键值,有3个键值(满了,需要分裂节点)的情况if (parent1->num == 1) //父节点没满且只有一个值{parent1->num++;if (parent1->key[0] < value) //新值在右边添加的情况直接在右边补上值和指针{parent1->key[1] = value;parent1->ptr[2] = newnode;}else//新的值在左边的话让原来的值右移一位{parent1->key[1] = parent1->key[0]; //右移一位parent1->key[0] = value;parent1->ptr[2] = parent1->ptr[1];parent1->ptr[1] = newnode;//ptr[0]仍然指向原来的那个键值较小的节点}}//父节点没满且有两个值else if (parent1->num == 2)//情况同上只是多分析一种{parent1->num++;if (parent1->key[1] < value) //新值在右边添加的情况直接在右边补上值和指针{parent1->key[2] = value;parent1->ptr[3] = newnode;}else if (parent1->key[0] < value) //在原来的两个值中插入{parent1->key[2] = parent1->key[1];parent1->key[1] = value;parent1->ptr[3] = parent1->ptr[2];parent1->ptr[2] = newnode;}else//新值在左边添加的情况{parent1->key[2] = parent1->key[1];parent1->key[1] = parent1->key[0];parent1->key[0] = value;parent1->ptr[3] = parent1->ptr[2];parent1->ptr[2] = parent1->ptr[1];parent1->ptr[1] = newnode;}}else//父节点满了父节点也要分裂{insert3(parent1, newnode);}}//添加数据键值函数3,即情况3:在情况2的基础上,内层节点已满,也需要分裂void BTree::insert3(Node * cur, Node * b){int i;Node * parent1 = cur->parent;//内层节点的父节点,也就是根节点if (parent1->num == 3) //根节点满了提示,没有再继续分根节点满了再分裂的情况{cout << "树满了无法继续添加该值" << endl;}else{//让需要分裂的n节点的后两个值分裂出去称左边的为左父亲int newkey[2] = { cur->key[1], cur->key[2] };cur->num = 1;Node * newnode = new Node(2, newkey);newnode->parent = parent1;//新分出来的内层节点的父节点是根节点//先获取原来的3个指针指向的节点// Node * node1 = cur->ptr[0];Node * node2 = cur->ptr[1];Node * node3 = cur->ptr[2];Node * node4 = cur->ptr[3];int a1 = b->key[0], c[3] = { node2->key[0], node3->key[0], node4->key[0] };/*a1是插入叶节点对应分出来的新节点的最小键值,c[3]是分裂内层节点前的后三个指针*/for (i = 0; i < 3; i++){if (a1 < c[i])break;}//以上方法是为了知道分裂出来的叶节点在所有5个子节点中的位置但是该位置必定不会是第一个,所以4种情况//注:为什么不会是第一个,因为上面分裂叶节点时总是将新节点指向较大键值的后面的节点//i是叶节点分裂出来的新节点的最小键值对应的内层节点应该插入比他稍大一点的最小键值的位置switch (i){case 0: //第一种情况新节点在左父亲的第二个指针另外三个节点在右父亲上cur->ptr[1] = b;b->parent = cur;newnode->ptr[0] = node2;newnode->ptr[1] = node3;newnode->ptr[2] = node4;node2->parent = newnode;node3->parent = newnode;node4->parent = newnode;break;case 1: //之后的情况依次将新节点b与右边节点交换cur->ptr[1] = node2;node2->parent = cur;newnode->ptr[0] = b;newnode->ptr[1] = node3;newnode->ptr[2] = node4;b->parent = newnode;node3->parent = newnode;node4->parent = newnode;break;case 2:cur->ptr[1] = node2;node2->parent = cur;newnode->ptr[0] = node3;newnode->ptr[1] = b;newnode->ptr[2] = node4;b->parent = newnode;node3->parent = newnode;node4->parent = newnode;break;case 3:cur->ptr[1] = node2;node2->parent = cur;newnode->ptr[0] = node3;newnode->ptr[1] = node4;newnode->ptr[2] = b;b->parent = newnode;node3->parent = newnode;node4->parent = newnode;break;default:break;}newnode->key[0] = newnode->ptr[1]->key[0];newnode->key[1] = newnode->ptr[2]->key[0];//在根节点添加键值//之后的代码与叶子的添加函数相同 value为右父亲的最小键值Node * parent1 = cur->parent;int value = newnode->ptr[0]->key[0];if (parent1->num == 1) //父节点没满且只有一个值{parent1->num++;if (parent1->key[0] < value) //新值在右边添加的情况直接在右边补上值和指针{parent1->key[1] = value;parent1->ptr[2] = newnode;}else//新的值在左边的话让原来的值右移一位{parent1->key[1] = parent1->key[0]; //右移一位parent1->key[0] = value;parent1->ptr[2] = parent1->ptr[1];parent1->ptr[1] = newnode;}}//父节点没满且有两个值else if (parent1->num == 2)//情况同上{parent1->num++;if (parent1->key[1] < value) //新值在右边添加的情况直接在右边补上值和指针{parent1->key[2] = value;parent1->ptr[3] = newnode;}else if (parent1->key[0] < value) //在原来的两个值中插入{parent1->key[2] = parent1->key[1];parent1->key[1] = value;parent1->ptr[3] = parent1->ptr[2];parent1->ptr[2] = newnode;}else{parent1->key[2] = parent1->key[1];parent1->key[1] = parent1->key[0];parent1->key[0] = value;parent1->ptr[3] = parent1->ptr[2];parent1->ptr[2] = parent1->ptr[1];parent1->ptr[1] = newnode;}}}}//******************************************************************************删除键值void BTree::deleteKey(int id){Result re = search(id);if (!re.tag){cout << "树内不存在该值,删除失败" << endl;return;}//把要删除的数据所在的节点删除剩下的所有节点重新调整指针最后把那个节点里的其他数据添加进来//用链表的方式存放两层所有节点list<Node *> out1, out2; //out1 中间层链表 2叶子链表Node * top = root;int i, j, k, n1, n2;for (k = 0; k <= root->num; k++)//out1存入中间层的节点对应的指针{if (root->ptr[k] != NULL){out1.push_back(root->ptr[k]);}}list<Node *>::iterator iter1 = out1.begin(); //迭代器获取链表的位置相当于数组的a[iter1]for (i = 0; i < out1.size(); i++){Node *temp = *iter1;for (k = 0; k <= temp->num; k++){if (temp->ptr[k] != NULL){if (temp->ptr[k]!= re.nptr )//把除了要删除的节点外的所有节点添加到了out2中来out2.push_back(temp->ptr[k]);}elsebreak;}iter1++;}n1 = out1.size(); n2 = out2.size(); //n1记录中间层节点个数 n2叶子个数-1 n2里不包括要删除的块list<Node *>::iterator iter2 = out2.begin(), iter3 = out1.begin(); //两个迭代器获取两条链表的首位iter1 = out1.begin();for (i = 0; i < n1; i++){Node * temp = *iter3;if (temp->num >1){if (temp->num > 2){temp->key[2] = NULL;temp->num--;break;}else{temp->key[1] = NULL;temp->num--;break;}}iter3++;} //这个循环先把有两个以上数据的中间层减少一个数据因为叶子少了一个块中间层必须少掉一个数据if (i == n1){cout << "数据太少,已经无法删除了" << endl;return;}//此时节点全部乱了让中间层的节点添加叶子节点根节点添加中间层节点for (i = 0; i < n1; i++) //中间层添加叶子{Node * temp = *iter1;for (j = 0; j <= temp->num; j++){Node * cur = *iter2;temp->ptr[j] = cur;cur->parent = temp;iter2++;}iter1++;}iter1 = out1.begin(); //重新回首地址for (j = 0; j <= root->num; j++){Node * cur = *iter1;root->ptr[j] = cur;cur->parent = root;iter1++;}iter1 = out1.begin(); iter2 = out2.begin(); //重新回首地址for (i = 0; i < root->num; i++){root->key[i] = root->ptr[i + 1]->ptr[0]->key[0];}for (i = 0; i < n1; i++){Node * temp = *iter1;for (j = 0; j < temp->num; j++)temp->key[j] = temp->ptr[j + 1]->key[0];iter1++;}//至此把要删除的块拿走后树的其余部分重新指向完成了//把多删除的数据用添加函数加回if (re. nptr ->num == 2) //叶子节点内只有两种情况两个数据或三个数据{if (re. nptr ->key[0] != id)insert1(re. nptr ->key[0]);if (re. nptr ->key[1] != id)insert1(re. nptr ->key[1]);}else{if (re. nptr ->key[0] != id)insert1(re. nptr ->key[0]);if (re. nptr ->key[1] != id)insert1(re. nptr ->key[1]);if (re. nptr ->key[2] != id)insert1(re. nptr ->key[2]);}}//******************************************************************************展示树void BTree::printTree(){queue<Node *> out;Node * parent1=NULL;Node * parent2 = NULL;if (root == NULL){cout << "当前B树为空!" << endl;return;}out.push(root);cout << "/*********************************输出B树**************************************/"<< endl;int depth = 1;while (out.size() != 0){cout << "第" << depth << "层:";int count = out.size();for (int i = 0; i < count; i++){Node *temp = out.front();out.pop();if (i == 0)parent1 = temp->parent;else{parent2 = temp->parent;if (parent1 == parent2);else{cout << "----" << " ";parent1 = parent2;}}for (int j = 0; j < temp->num; j++){cout << "[" << temp->key[j] << "]";}cout << " ";for (int k = 0; k <= temp->num; k++){if (temp->ptr[k] != NULL){out.push(temp->ptr[k]);}elsebreak;}}depth++;cout << endl;}cout << "/********************************B树输出结束***********************************/" << endl << endl;}文件源.cpp#include"BTree.h"#include<fstream>#include<sstream>list<Student *> stu;//学生链表list<Student *>::iterator it;//迭代器BTree tree;//建树//读入datafile.txt数据初始化B树void input(){string record;string student;ifstream in2("datafile.txt"); //datafile文件读入操作while (getline(in2, record)) //从文件一行一行读入信息{//char a; int b;istringstream sin(record);string id, fname, lname, grade, major, mail;sin >> id >> fname >> lname >> grade >> major >> mail; //字符串拆分操作int sid = atoi(id.c_str());int sgrade = atoi(grade.c_str());tree.insert1(sid);Student * newstu = new Student(id, fname, lname, sgrade, major, mail);stu.push_back(newstu);}in2.close();ifstream in1("commond.txt"); //commond文件读入操作while (getline(in1, record)) //从文件一行一行读入信息{string a; int b;istringstream sin(record);sin >> a >> b; //字符串拆分操作if (pare("add") == 0){tree.insert1(b);string c1, c2, c3, c5, c6; int c4;sin >> c1 >> c2 >> c3 >> c4 >> c5 >> c6;Student * newstu = new Student(c1, c2, c3, c4, c5, c6);stu.push_back(newstu);}if (pare("delete") == 0){tree.deleteKey(b);}}in1.close();}//数据输出到studentDatavoid output(){int i;freopen("datafile.txt", "w", stdout);it = stu.begin();for (i = 0; i < stu.size(); i++){Student * cur = *it;cout << cur->StudentId << " " << cur->StudentFirstName << " " <<cur->StudentLastName << " "<< cur->grade << " "<< cur->Major << " "<< cur->email << endl;it++;}fclose(stdout);}// 查找学生idvoid findID(int t,int k){int id,i;if (t == 1) //菜单调用{cout << endl << "------------->" << "find ID" << "<--------------" << endl;cout << endl << "请输入学生的整数型ID:";cin >> id;}else//范围查找时调用id = k;Result a = tree.search(id);if (a.tag) //找到了{it = stu.begin();for (i = 0; i < stu.size(); i++){Student * cur = *it;if (cur->Id == id){cout<< "ID : " << cur->StudentId << endl<< "姓: " << cur->StudentFirstName << endl<< "名: " << cur->StudentLastName << endl<< "年级: " << cur->grade << endl<< "专业: " << cur->Major << endl<< "邮箱: " << cur->email << endl;return;}it++;}}else{if (t==1)cout << "您查找的数据不存在" << endl;return;}}// 范围查找void findIDRange(){int id1,id2, i;cout << endl << "------------->" << "find IDmin,IDmax" << "<--------------" << endl;cout << endl << "请分别输入学生的整数型ID下界和上界:";cin >> id1 >> id2;if (id1 > id2)return;for (i = id1; i <= id2; i++) //从下界开始查找每次加一findID(0,i);cout << "查找完毕" << endl;return;}// 插入学生信息void add(){int id;string idStr;cout << endl << "------------->" << "add" << "<--------------" << endl;cout << endl << "请输入学生ID:"<<endl;cin >> idStr;id = atoi(idStr.c_str());if (id == -1){cout << "该id不符合要求" << endl;return;}Result a = tree.search(id);if (a.tag){cout << "该id已经存在" << endl;return;}else{string fname, lname, major, email;int grade;cout << "请输入学生姓" << endl; cin >> fname;cout << "请输入学生名" << endl; cin >> lname;cout << "请输入学生年级" << endl; cin >> grade;cout << "请输入学生专业" << endl; cin >> major;cout << "请输入学生邮箱" << endl; cin >> email;tree.insert1(id);Student * newstu = new Student(idStr, fname, lname, grade, major, email);stu.push_back(newstu);cout << "添加成功" << endl;//把命令写出去ofstream out1("commond.txt", ios::app);out1 << "add" << " " << id << " " << fname << " " << lname << " " << grade << " " << major << " " << email << endl;out1.close();ofstream out2("datafile.txt", ios::app);cout << endl;out2 << id << " " << fname << " " << lname << " " << grade << " " << major << " " << email << endl;out2.close();return;}}// 删除学生信息void deleteStudent(){int id,i;string idStr;cout << endl << "------------->" << "delete" << "<--------------" << endl;cout << endl << "请输入学生ID:" << endl;cin >> idStr;id = atoi(idStr.c_str());;if (id == -1){cout << "该id不符合要求" << endl;return;}Result a = tree.search(id);if (a.tag){tree.deleteKey(id);it = stu.begin();for (i = 0; i < stu.size(); i++){Student * cur = *it;if (cur->Id == id){stu.erase(it);return;}it++;}cout << "删除成功" << endl;//把命令写到commond.txtofstream out("commond.txt", ios::app);out << "delete" << " " << id << endl;out.close();return;}else{cout << "该id不存在" << endl;return;}}//菜单void Menu(){char ch;while (true){bool exit = false;cout << endl;cout<< "====================== Menu ======================" << endl;cout << endl;cout<< "1. find ID" << endl<< "2. find IDmin,IDmax" << endl<< "3. add" << endl<< "4. delete" << endl<< "5. printBTree" << endl<< "6. exit" << endl;cout << endl << "请选择:";while (cin >> ch){bool back = true;switch (ch){case'1': findID(1,0); break;case'2': findIDRange(); break;case'3': add(); break;case'4': deleteStudent(); break;case'5': tree.printTree(); break;case'6': exit = true; break;数据库系统实现default: cout << "输入错误,请重新输入:"; back = false; break;}if (back){break;}}if (exit){break;}}}void main(){input();//输入文件Menu(); //菜单output(); //写文件}。