⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hashfile.cpp

📁 hash 文件操作(数据结构)
💻 CPP
字号:
#include<iostream.h>
#include<stdlib.h>
#include<fstream.h>

//m为散列表的长度,假定取值为13
  const int m=13;  

//kn为散列主文件中每个结点所含的元素个数,
//取值大于等于1,这里假定取为3
  const int kn=3;  
//定义关键字类型为整型
  typedef int KeyType;  
//索引主文件中的记录元素类型
  struct ElemType {   
  	  KeyType key;    //关键字域  
	  char rest[10];  //其他域,假定用字符数组表示
  };
//索引主文件中的结点类型
  struct FLNode {  
	  ElemType data[kn];  //值域
	  int next;           //指向下一个结点的指针域
  };
//b1为索引表文件中的记录长度
  const int b1=sizeof(int); 
//b2为索引主文件中的记录长度
  const int b2=sizeof(FLNode); 
//NullTag作为空关键字的标记,假定用-100表示
  const int NullTag=-100;  

//初始化散列表文件和主文件
void InitHashFile(char* fname1, char* fname2)
{
  //以输出方式打开并建立空的散列表文件
	ofstream f1(fname1, ios::out|ios::binary);
	if(!f1) {
		cerr<<fname1<<' '<<"not open!"<<endl;
		exit(1);
	}
  //以输出方式打开并建立空的散列主文件
	ofstream f2(fname2, ios::out|ios::binary);
	if(!f2) {
		cerr<<fname2<<' '<<"not open!"<<endl;
		exit(1);
	}
 //动态分配具有m+1个整型存储空间的数组A
   int* A=new int[m+1];
	if(!A) {
		cerr<<"Memory allocation failare!"<<endl;
		exit(1);
	}
  //给数组A中的每个元素赋初值-1,表示空指针
	for(int i=0; i<m+1; i++)
		A[i]=-1;  
  //初始化散列表文件
	f1.write((char*)A, (m+1)*b1);
  //删除数组A并关闭文件
	delete [] A;
	f1.close();
	f2.close();
}

//把元素x插入到按桶散列的文件中
void THFInsertA(char* fname1, char* fname2, ElemType x)
{
  //以输入输出和不新建方式打开散列表文件
	fstream f1(fname1, ios::in|ios::out|ios::nocreate|ios::binary);
	if(!f1) {
		cerr<<fname1<<' '<<"not open!"<<endl;
		exit(1);
	}
  //以输入输出和不新建方式打开散列主文件
	fstream f2(fname2, ios::in|ios::out|ios::nocreate|ios::binary);
	if(!f2) {
		cerr<<fname2<<' '<<"not open!"<<endl;
		exit(1);
	}
  //给数组A中的每个元素赋初值-1,表示空指针
	int* A=new int[m+1];
	if(!A) {
		cerr<<"Memory allocation failare!"<<endl;
		exit(1);
	}
  //将散列表文件中的内容全部读入到数组A中
	f1.read((char*)A, (m+1)*b1);
  //以关键字x.key计算x的散列地址
	int d=x.key%m;
  //将x插入到d单链表的表头结点中
	int j;
	FLNode temp;
	if(A[d]!=-1)
	{
		f2.seekg(A[d]*b2);
		f2.read((char*)&temp, b2);
		for(j=0; j<kn; j++)
			if(temp.data[j].key==NullTag)
				break;
		if(j<kn) {
			temp.data[j]=x;
			f2.seekg(-b2, ios::cur);
			f2.write((char*)&temp, b2);
			goto END;  //插入表头结点后,转去做结束处理
		}
	}
  //当d单链表为空或头结点中没有空闲位置时向下执行
  //建立待插入d单链表表头的内存结点temp
	temp.data[0]=x;
	for(j=1; j<kn; j++)
		temp.data[j].key=NullTag;
	temp.next=A[d];
  //将temp结点的值写入到f2文件尾并被链接到d单链表的表头
	if(A[m]==-1) {
	  //将f2中的文件指针移至文件尾
		f2.seekg(0,ios::end);
	  //计算出文件尾的结点位置序号
		int len=f2.tellg()/b2;
	  //将temp结点的值写入文件尾
		f2.write((char*)&temp,b2);
	  //使A[d]指向新插入的结点
		A[d]=len;
	}
  //将temp结点的值写入到f2文件的一个空闲结点中
  //并被链接到d单链表的表头
	else {
      //p指向空闲单链表的表头结点
		int p=A[m];  
      //使空闲单链表的表头指针指向其下一个结点
		f2.seekg(p*b2);
		FLNode pn;
		f2.read((char*)&pn, b2);
		A[m]=pn.next;  
	  //使temp的值写入到p位置的结点上
		f2.seekg(-b2, ios::cur);
		f2.write((char*)&temp, b2);  
	  //使A[d]指向新插入的p结点
		A[d]=p;
	}
  //将数组A中的全部内容写回到散列表文件f1中
	f1.seekg(0);
    f1.write((char*)A, (m+1)*b1);
  //删除动态数组A和关闭所有文件
    END:
	delete [] A;
	f1.close();
	f2.close();
}

//把数组x中n个元素插入到按桶散列的文件中
void THFInsertB(char* fname1, char* fname2, ElemType x[], int n)
{
	fstream f1(fname1, ios::in|ios::out|ios::nocreate|ios::binary);
	if(!f1) {
		cerr<<fname1<<' '<<"not open!"<<endl;
		exit(1);
	}
	fstream f2(fname2, ios::in|ios::out|ios::nocreate|ios::binary);
	if(!f2) {
		cerr<<fname2<<' '<<"not open!"<<endl;
		exit(1);
	}
    int* A=new int[m+1];
	if(!A) {
		cerr<<"Memory allocation failare!"<<endl;
		exit(1);
	}
	f1.read((char*)A, (m+1)*b1);
	for(int i=0; i<n; i++)
	{
	    int d=x[i].key%m;
     	int j;
	    FLNode temp;
	    if(A[d]!=-1)
		{
		    f2.seekg(A[d]*b2);
		    f2.read((char*)&temp, b2);
		    for(j=0; j<kn; j++)
			    if(temp.data[j].key==NullTag)
				    break;
			if(j<kn) {
			    temp.data[j]=x[i];
			    f2.seekg(-b2, ios::cur);
			    f2.write((char*)&temp, b2);
			    continue;
			}
		}
    	temp.data[0]=x[i];
	    for(j=1; j<kn; j++)
		    temp.data[j].key=NullTag;
	    temp.next=A[d];
	    if(A[m]==-1) {
		    f2.seekg(0,ios::end);
		    int len=f2.tellg()/b2;
		    f2.write((char*)&temp,b2);
		    A[d]=len;
		}
	    else {
		    int p=A[m];  
		    f2.seekg(p*b2);
		    FLNode pn;
		    f2.read((char*)&pn, b2);
		    A[m]=pn.next;  
		    f2.seekg(-b2, ios::cur);
		    f2.write((char*)&temp, b2);  
		    A[d]=p;
		}
	}
	f1.seekg(0);
    f1.write((char*)A, (m+1)*b1);
	delete [] A;
	f1.close();
	f2.close();
}

//从按桶散列的文件中删除关键字为x.key的元素,并由x带回该
//元素,若删除成功则返回1,否则返回0
int THFDelete(char* fname1, char* fname2, ElemType& x)
{
	fstream f1(fname1, ios::in|ios::out|ios::nocreate|ios::binary);
    if(!f1) {
		cerr<<fname1<<' '<<"not open!"<<endl;
		exit(1);
	}
	fstream f2(fname2, ios::in|ios::out|ios::nocreate|ios::binary);
	if(!f2) {
		cerr<<fname2<<' '<<"not open!"<<endl;
		exit(1);
	}
    int* A=new int[m+1];
	if(!A) {
		cerr<<"Memory allocation failare!"<<endl;
		exit(1);
	}
	f1.read((char*)A, (m+1)*b1);
    int DeleteTag=0;  //用DeleteTag作为删除是否成功的标记
	int d=x.key%m;
    int p=A[d],i=0;  //用p保存d单链表的表头结点的位置序号,
	    //用i保存该结点中被删除元素的下标或第一个空元素的下标
    FLNode temp;
  //从d单链表的表头结点中删除关键字为x.key的元素
    if(p!=-1)
	{
	    f2.seekg(p*b2);
		f2.read((char*)&temp, b2);
		while(i<kn && temp.data[i].key!=NullTag)
			if(temp.data[i].key==x.key)
				break;
			else 
				i++;
		if(i<kn && temp.data[i].key==x.key) {
            x=temp.data[i];  //由x带回被删除的元素值
			for(int k=i+1; k<kn; k++)
				if(temp.data[k].key!=NullTag)
					temp.data[k-1]=temp.data[k];
				else
					break;
			temp.data[k-1].key=NullTag;
			if(k-1==0) {  //删除d单链表中表头空结点
				A[d]=temp.next;
				temp.next=A[m];
				A[m]=p;
			}
			f2.seekg(-b2, ios::cur);
			f2.write((char*)&temp, b2);
			DeleteTag=1;  //DeleteTag被赋值1表示删除成功
			goto END;  //转去做结束处理
		}
	}
  //若d单链表为空,则转去做结束处理
	else
		goto END;
  //从d单链表的第二个结点起顺序查找被删除的元素
	int j;  
	int q; 
    q=temp.next;  //q初始指向第二个结点
	FLNode tq;  //用tq保存散列主文件中位置为q的结点
	while(q!=-1) 
	{
	    f2.seekg(q*b2);
		f2.read((char*)&tq, b2);
		for(j=0; j<kn; j++)
			if(tq.data[j].key==x.key) {
				x=tq.data[j];
				break;
			}
        if(j<kn) break;
		q=tq.next;
	}
  //若找到了被删除的元素,则将第一个结点中的最后一个元素
  //移动到被删除元素的位置
	if(q!=-1) {
		tq.data[j]=temp.data[i-1];
		temp.data[i-1].key=NullTag;
		f2.seekg(q*b2);
		f2.write((char*)&tq, b2);
		if(i==1) {  //把空的表头结点链接到空闲表的表头
			A[d]=temp.next;
			temp.next=A[m];
			A[m]=p;
		}
		f2.seekg(p*b2);
		f2.write((char*)&temp, b2);
		DeleteTag=1;  //置删除成功标志
	}
  //做删除操作的结束处理操作
    END:
	if(DeleteTag==1) {
		f1.seekg(0);
        f1.write((char*)A, (m+1)*b1);
	}
	delete [] A;
	f1.close();
	f2.close();
	if(DeleteTag==1) return 1; else return 0;
}

//从按桶散列的文件中查找关键字为x.key的元素,并由x带回该
//元素,若查找成功则返回1,否则返回0
int THFSearch(char* fname1, char* fname2, ElemType& x)
{
  //以输入和不新建方式打开散列表文件
	ifstream f1(fname1, ios::in|ios::nocreate|ios::binary);
	if(!f1) {
		cerr<<fname1<<' '<<"not open!"<<endl;
		exit(1);
	}
  //以输入和不新建方式打开散列主文件
	ifstream f2(fname2, ios::in|ios::nocreate|ios::binary);
	if(!f2) {
		cerr<<fname2<<' '<<"not open!"<<endl;
		exit(1);
	}
  //定义数组A并将f1文件读入A中
    int* A=new int[m+1];
	if(!A) {
		cerr<<"Memory allocation failare!"<<endl;
		exit(1);
	}
	f1.read((char*)A, (m+1)*b1);
  //计算x元素在散列表中的地址
	int d=x.key%m;
  //从d单链表中顺序查找关键字为x.key的元素
	int p=A[d];
  //从d单链表中顺序查找关键字为x.key的元素
	FLNode temp;
	while(p!=-1)
	{
		f2.seekg(p*b2);
		f2.read((char*)&temp, b2);
		for(int i=0; i<kn; i++) 
			if(temp.data[i].key==x.key) {
      			x=temp.data[i]; //被查找到的元素由x带回
				break;
			}
		if(i<kn) 
			break;
		else
			p=temp.next;
	}
  //进行查找结束处理
	delete [] A;
	f1.close();
	f2.close();
    if(p==-1) return 0; else return 1;
}

//按单链表顺序打印出按桶散列主文件中每个结点的所有元素值
void THFPrint(char* fname1, char* fname2)
{
	ifstream f1(fname1, ios::in|ios::nocreate|ios::binary);
	if(!f1) {
		cerr<<fname1<<' '<<"not open!"<<endl;
		exit(1);
	}
	ifstream f2(fname2, ios::in|ios::nocreate|ios::binary);
	if(!f2) {
		cerr<<fname2<<' '<<"not open!"<<endl;
		exit(1);
	}
    int* A=new int[m+1];
	if(!A) {
		cerr<<"Memory allocation failare!"<<endl;
		exit(1);
	}
	f1.read((char*)A, (m+1)*b1);
	int p;
	FLNode pn;
    for(int i=0; i<m+1; i++)
	{
		cout<<i<<": ";
		p=A[i];
		while(p!=-1) {
			f2.seekg(p*b2);
			f2.read((char*)&pn, b2);
  	      //输出结点的位置序号
			cout<<p<<"->";
		  //输出结点中每个元素的关键字,以此代替结点的值
			for(int j=0; j<kn; j++) 
			    cout<<pn.data[j].key<<' ';
			cout<<"    ";
		  //使p指向下一个结点
			p=pn.next;
		}
		cout<<endl;
	}
}

void main()
{
  //定义保存记录的数组a并初始化
	ElemType a[15]={{12,"liu"},{18,"quhua"},{15,"zhp"},{32,"renwm"},
	{6,"xcong"},{23,"whua"},{21,"luosh"},{48,"tian"},{36,"chen"},
	{57,"shdm"},{45,"kang"},{29,"lishu"},{25,"taosh"},
	{38,"dingx"},{14,"yuwen"}};
  //定义保存记录的数组b并初始化
	ElemType b[16]={{27,"chen"},{42,"huang"},{60,"tongy"},
	{55,"zhao"},{87,"caik"},{74,"ganwu"},{69,"duli"},{46,"yinj"},
	{26,"huche"},{41,"qiuh"},{73,"baota"},{70,"zhoup"},
	{96,"zhx"},{67,"luyx"},{44,"yugang"},{9,"wxy"}};
  //定义散列表文件和主文件的名字,并由字符指针p1和p2所指向  
	char *p1="HashFile2.idx", *p2="HashFile2.dat";
  //初始化散列表文件和主文件	
	InitHashFile(p1,p2);
  //向散列文件插入数组a和b中保存的记录  
	THFInsertB(p1,p2,a,15);
	THFInsertB(p1,p2,b,16);
  //定义tag用于保存删除或查找函数返回的值
	int tag;  
  //定义x保存一个待插入的元素值或保存待删除或查找元素的关键字
	ElemType x;  
  //利用键盘输入操作散列文件
	while(1) {
	    cout<<endl<<"功能号表:"<<endl;
	    cout<<"1---向散列文件插入一个元素"<<endl;
	    cout<<"2---从散列文件中删除一个元素"<<endl;
	    cout<<"3---从散列文件中查找一个元素"<<endl;
		cout<<"4---打印散列主文件"<<endl;
		cout<<"5---结束运行"<<endl;
	    char ch;
	    cout<<"请输入你的选择(1-5): ";
	    cin>>ch;
	    switch (ch)
		{
	    case '1':
	        cout<<"输入待插入元素x的值(一个整数和一个字符串):";
	        cin>>x.key>>x.rest;
            THFInsertA(p1,p2,x);
            break;
	    case '2':
	        cout<<"输入待删除元素x的关键字:";
	        cin>>x.key;
			tag=THFDelete(p1,p2,x);
            if(tag==1)
				cout<<"删除成功!"<<x.key<<' '<<x.rest<<endl;
			else
				cout<<"删除失败!"<<endl;
			break;
		case '3':
	        cout<<"输入待查找元素x的关键字:";
	        cin>>x.key;
			tag=THFSearch(p1,p2,x);
            if(tag==1)
				cout<<"查找成功!"<<x.key<<' '<<x.rest<<endl;
			else
				cout<<"查找失败!"<<endl;
			break;
		case '4':
			THFPrint(p1,p2);
			break;
		case '5':
		    return;
		default:
			cout<<"输入选择错误,请重输!"<<endl;
		}
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -