题目
功能要求:
(1)初始化散列文件;
(2)向散列文件中插入一个元素;
(3)从散列文件中删除一个元素;
(4)从散列文件中查找一个元素。
散列文件通常采用链接法处理冲突。
散列文件中每个节点的类型定义为:
Struct FLNode
{ //散列主文件中的节点类型
ElemType data ; //值域
int next; //指向下一个节点的指针域
};
代码
#include <bits/stdc++.h>
using namespace std;
const int N=20; //以去留余数法创建散列表
typedef int KeyType;
struct Elemtype
{
KeyType key; //存储关键信息
char rest[N]; //存储附加信息
};
struct FLNode
{
Elemtype data; //值域
int next; //指针域
};
char filename[]="Hash"; // 散列主文件名
char Iname[]="HashIndex"; // 索引文件名
const int b1 = sizeof(KeyType); //索引表文件中的元素长度
const int b2 = sizeof(struct FLNode); //索引主文件中的结点长度
void welcome(); //欢迎使用
int start(); //模式选择函数
void existHash(char *fname); //检测是否存在散列文件
void InitHashFile(char *fname); //初始化散列文件
void HashFilePrint(char *fname); //打印散列文件
void HashFileInsert(char filename[]); //插入一个元素
bool HashFileSearch(char *fname,struct Elemtype *x); //在散列文件中查找元素并返回查找结果
void HashFileInOne(char *fname,struct Elemtype x); //向散列文件中插入一个元素
void HashSearch(char *fname); //查找一个元素
bool HashDelete(char *fname,struct Elemtype *x); //在散列表中删除一个元素
void HashFileDelete(char *fname); //删除一个元素
int main()
{
welcome();
while(1)
{
int choose=start();
if(choose==0) break;
else if(choose==1) InitHashFile(filename); //进行初始化操作
else if(choose==5) HashFilePrint(filename); //打印散列文件
else if(choose==2) HashFileInsert(filename); //向散列文件中插入一个元素
else if(choose==4) HashSearch(filename); //查找一个元素
else if(choose==3) HashFileDelete(filename); //删除一个元素
else cout<<"您输入的指令有误,请重新输入!"<<endl<<endl;
}
cout<<"****已退出系统,感谢您的使用!****"<<endl<<endl;
return 0;
}
bool HashDelete(char *fname,struct Elemtype *x) //在散列表中删除一个元素
{
FILE *fp1, *fp2;
fp1 = fopen(fname, "rb+");
fp2 = fopen(Iname, "rb+");
if(NULL == fp1|| NULL == fp2)
{
printf("打开文件失败!\n");
exit(1);
}
int *A;
A = (int *)calloc(N+1, b1);
if(NULL == A)
{
printf("内存申请失败!\n");
exit(1);
}
fread((char *)A, (N+1)*b1, 1, fp2);
int d = x->key % N;
int p = A[d],q;
struct FLNode tp,tq;
while(-1 != p)
{
fseek(fp1, (N+1)*b1+p*b2, 0);
fread((char*)&tp, b2, 1, fp1);
if(tp.data.key == x->key)
{
//被删除结点的元素值赋给x带回
*x = tp.data;
//从单链表中删除p结点
if(p == A[d])
A[d] = tp.next;
else
{
tq.next = tp.next;
fseek(fp1, (N+1)*b1+q*b2, 0);
fwrite((char *)&tq, b2, 1, fp1);
}
//将p结点连接到空闲单链表的表头
tp.next = A[N];
fseek(fp1, (N+1)*b1+p*b2, 0);
fwrite((char *)&tp, b2, 1,fp1);
A[N] = p;
//结束while循环
break;
}
else
{
//使p指针的值赋给q,tp结点的值赋值给tq结点
q = p;
tq = tp;
//p指向单链表中的下一个结点
p = tp.next;
}//if分支结束
}//while循环结束
//将索引文件的表头重新写入索引文件
fseek(fp1, 0L, 0);
fseek(fp2, 0L, 0);
fwrite((char *)A, (N + 1) * b1, 1, fp2);
//释放A数组申请的内存空间
free(A);
//关闭散列文件
fclose(fp1);
fclose(fp2);
if(-1 == p)
return false;
else
return true;
}
void HashFileDelete(char *fname) //删除一个元素
{
struct Elemtype x;
cout<<"请输入待删除元素的关键信息(序号):";
cin>>x.key;
cout<<endl;
if(!HashFileSearch(filename,&x)) cout<<"该元素在散列文件中不存在,不需要删除"<<endl<<endl;
else
{
bool mark=HashDelete(filename,&x);
if(mark==1) cout<<"关键元素(序号)为"<<x.key<<"的元素删除成功"<<endl<<endl;
else cout<<"关键元素(序号)为"<<x.key<<"的元素删除失败"<<endl<<endl;
}
}
void HashSearch(char *fname) //查找一个元素
{
struct Elemtype x;
cout<<"请输入待插入元素的关键信息(序号):";
cin>>x.key;
cout<<endl;
if(HashFileSearch(filename,&x)) cout<<"该元素在散列文件中存在"<<endl<<endl;
else cout<<"该元素在散列文件中不存在"<<endl<<endl;
}
void HashFileInOne(char *fname,struct Elemtype x) //向散列文件中插入一个元素
{
FILE *fp1, *fp2;
fp1 = fopen(fname, "rb+");
fp2 = fopen(Iname, "rb+");
if(NULL == fp1)
{
printf("打开文件失败!\n");
exit(1);
}
int *A;
A = (int *)calloc(N + 1, b1);
if(!A)
{
printf("内存申请失败!\n");
exit(1);
}
fread((char *)A, (N + 1) * b1, 1, fp2);
int d = x.key % N;
//以x和A[d]的值构成待插入散列文件的内存节点temp
struct FLNode temp,pn;
temp.data = x;
temp.next = A[d];
//将temp结点的值写入到散列文件中,并链接到散列文件表头
//下表d单链表的表头
int p,len;
if(-1 == A[N])
{
//将文件指针移至文件尾
fseek(fp1, 0L, 2);
//计算出文件尾的结点位置序号
len = (ftell(fp1) - (N+1)*b1)/b2;
//将temp结点的值写入文件尾
fwrite((char *)&temp, b2, 1, fp1);
//使A[d]指向新插入的结点
A[d] = len;
}
else
{
//p指向空闲单链表的表头结点
p = A[N];
//使空闲单链表的表头指针指向其下一个结点
fseek(fp1, b1 * (N+1) + p*b2, 0);
fread((char *)&pn, b2, 1, fp1);
A[N] = pn.next;
//使temp的值写入到p位置的结点上
fseek(fp1, -b2, 1);
fwrite((char *)&temp, b2, 1, fp1);
//使A[p]指向新插入的p结点
A[d] = p;
}
//将数组A中的全部内容写回到索引文件的表头中
fseek(fp1,0L,0);
fseek(fp2,0L,0);
fwrite((char *)A, b1 * (N+1), 1, fp2);
free(A);
fclose(fp1);
fclose(fp2);
}
bool HashFileSearch(char *fname,struct Elemtype *x) //在散列文件中查找元素并返回查找结果
{
struct FLNode temp;
FILE *fp1, *fp2;
fp1 = fopen(fname, "rb+");
fp2 = fopen(Iname, "rb+");
if(NULL == fp1|| NULL == fp2)
{
printf("打开文件失败!\n");
exit(1);
}
int *A;
A = (int *)calloc(N+1, b1);
if(NULL == A)
{
printf("内存申请失败!\n");
exit(1);
}
fread((char *)A, (N+1)*b1, 1, fp2);
int d = x->key % N;
int p = A[d];
while(p != -1)
{
fseek(fp1, (N+1)*b1 + p*b2, 0);//在文件中定位
fread((char *)&temp, b2, 1, fp1);
if(temp.data.key == x->key)
{
*x = temp.data; //被查找到的元素由x带回
break;
}
else
p = temp.next;
}
free(A);
fclose(fp1);
fclose(fp2);
if(-1 == p) return false;
else return true;
}
void HashFileInsert(char filename[]) //向散列文件中插入一个元素
{
struct Elemtype x;
cout<<"请输入待插入元素的关键信息(序号):";
cin>>x.key;
cout<<endl;
cout<<"请输入待插入元素的附加信息(字符):";
cin>>x.rest;
if(HashFileSearch(filename,&x)) cout<<"该元素在散列文件中已经存在,无需再次插入!"<<endl<<endl;
else
{
cout<<"该元素在散列文件中不存在"<<endl<<endl;
HashFileInOne(filename,x);
cout<<"关键元素(序号)为"<<x.key<<"的元素经插入完成!"<<endl<<endl;
}
}
void HashFilePrint(char *fname) //打印散列文件
{
cout<<"散列文件如下:"<<endl;
FILE *fp1, *fp2;
fp1 = fopen(fname, "rb+");
fp2 = fopen(Iname, "rb+");
if(NULL == fp1|| NULL == fp2)
{
cout<<"文件打开失败"<<endl;
exit(1);
}
int *A;
A = (int *)calloc(N+1, b1);
if(NULL == A)
{
cout<<"内存申请失败"<<endl;
exit(1);
}
fread((char *)A, b1, N+1, fp2);
int p;
struct FLNode pn;
for(int i = 0; i < N+1; i++)
{
printf("%d:", i);
p = A[i];
while(-1 != p)
{
fseek(fp1, (N+1)*b1 + p*b2, 0); // 修改文件指针位置
fread((char *)&pn, b2, 1, fp1); // 从文件中中读取节点
printf("%d->%d(%s) ", p, pn.data.key, pn.data.rest);
p = pn.next;
}
printf("\n");
}
free(A);
fclose(fp1);
fclose(fp2);
cout<<"输出结束!"<<endl<<endl;
}
void InitHashFile(char *fname) //初始化散列文件
{
cout<<"是否确认要初始化散列文件(0-退出 1-确认初始化):";
bool choose;
cin>>choose;
cout<<endl;
if(choose==0)
{
cout<<"****散列文件未进行初始化!"<<endl<<endl;
return;
}
cout<<"****开始进行散列文件的初始化!"<<endl<<endl;
FILE *fp1,*fp2;
int *A;
fp1 = fopen(fname, "wb+");
fp2 = fopen(Iname, "wb+");
if(NULL == fp1|| NULL == fp2)
{
printf("打开文件失败!/n");
exit(1);
}
//动态分配具有N+1个整型储存空间的数组A
A = (int *)calloc(N + 1, b1);
if(NULL == A)
{
printf("内存申请失败!/n");
exit(1);
}
//给数组A中的每个元素赋初值-1,表示空指针
for(int i = 0; i < N + 1; i++)
{
A[i] = -1;
}
//初始化散列文件
fwrite((char *)A, (N + 1)*b1, 1, fp1);
fwrite((char *)A, (N + 1)*b1, 1, fp2);
free(A);
fclose(fp1);
fclose(fp2);
cout<<"****散列文件初始化已完成!"<<endl<<endl;
}
void existHash(char *fname) //检测是否存在散列文件
{
FILE *fp1,*fp2;
fp1 = fopen(fname, "rb");
fp2 = fopen(Iname, "rb");
if(fp1==NULL||fp2==NULL)
cout<<"****系统中不存在散列文件,需要初始化散列文件后才可使用本系统****"<<endl<<endl;
else
{
cout<<"****系统中已存在散列文件,可以直接使用****"<<endl<<endl;
//只有存在散列文件的时候文件指针才有意义,才需要进行释放操作
fclose(fp1);
fclose(fp2);
}
}
void welcome() //欢迎使用
{
cout<<" 欢迎使用散列文件系统!"<<endl<<endl;
existHash(filename);
}
int start() //模式选择函数
{
int num;
printf("\t+----------------------------------+\n");
printf("\t|----------- 散列文件 -------------|\n");
printf("\t|--1-- 初始化散列文件 -------------|\n");
printf("\t|--2-- 向散列文件中插入一个元素 ---|\n");
printf("\t|--3-- 从散列文件中删除一个元素 ---|\n");
printf("\t|--4-- 从散列文件中查找一个元素 ---|\n");
printf("\t|--5-- 打印散列文件 ---------------|\n");
printf("\t|--0-- 结束运行 -------------------|\n");
printf("\t+----------------------------------+\n");
cout<<"****请输入您的选择(0-5):";
cin>>num;
cout<<endl;
return num;
}