具体实现的问题描述
给定散列函数的除数D和操作数m,输出每次操作后的状态。
有以下三种操作:
1. 插入x,若散列表已存在x,输出“Existed”,否则插入x到散列表中,输出所在的下标。
2. 查询x,若散列表不含有x,输出“-1”,否则输出x对应下标。
3. 删除x,若散列表不含有x,输出“Not Found”,否则输出删除x过程中移动元素的个数。
输入格式
第一行两个整数D(1\leq≤ D \leq≤ 3000)和m(1\leq≤ m \leq≤ 3000),其中D为散列函数的除数,m为操作数。
接下来的m行,每行两个整数opt和x,分别代表操作类型和操作数。
若opt为0,则代表向散列表中插入x;
若opt为1,代表查询散列表中x是否存在;
若opt为2,(如果散列表中含有x),删除x。
数据保证散列表不会溢出。
输出格式
m行,每行一个整数,代表对应查询的答案。
样例输入1
7 12
1 21
0 1
0 13
0 5
0 23
0 26
0 33
1 33
1 33
1 13
1 5
1 1
样例输出1
-1
1
6
5
2
0
3
3
3
6
5
1
样例输入2
7 15
2 10
0 10
0 10
2 10
1 10
0 10
1 10
0 17
0 2
0 16
0 11
2 2
2 10
1 11
1 17
样例输出2
Not Found
3
Existed
0
-1
3
3
4
2
5
6
2
2
4
3
首先先定义哈希表的私有变量和成员函数 HashTable.h
#ifndef SORTEDCHAIN_HASHTABLE_H
#define SORTEDCHAIN_HASHTABLE_H
template <class E,class K>
class HashTable{
public:
HashTable(int divisor);
~HashTable(){
delete []ht;
delete []empty;
}
int Search(const K &k);
int Insert(const E & e);
int Delete(const K &k);
void Output(std::ostream & put)const;
friend std::ostream &operator<<(std::ostream &out,const HashTable<E,K> & ht){
ht.Output(out);
return out;
}
private:
int hSearch(const K &k)const;
int D;
E *ht;
bool *empty;
};
#endif //SORTEDCHAIN_HASHTABLE_H
哈希表的构造函数 设置除数
template <class E,class K>
HashTable<E,K>::HashTable(int divisor) {
D=divisor; //读入除数
ht=new E[D]; //分配散列数组
empty=new bool[D]; //判断数组,对应散列数组,判断对应位置是否已经有元素
for(int i=0;i<D;i++){
empty[i]=true; //初始化判断数组
}
}
设置进行搜索的两个函数 具体实现搜索功能的是
search()函数,hsearch()函数对于后续进行删除操作,确定一个特定元素在哈希表中的 实际位置 和 理论位置 具有重要作用
template <class E,class K>
int HashTable<E,K>::hSearch(const K &k) const {
//查找查找hash表中是否存在key=k的元素
// 如果存在,则返回k的位置j
// 否则如有足够空间,那么 j 表示可以插入的数组下标
int i=k%D; //i记录k在散列数组中的对应索引值,起始桶
int j=i;
do{
if(empty[j]||ht[j]==k) //2种情况直接返回数值j
return j;
j=(j+1)%D; //表为环形的,找到下一个桶
}while(j!=i);
return j; //回到起始桶
// Q:那回到起始桶和一开始就找到有什么区别?
//再次检查那个位置上的值,会发现回到起始桶的那种情况,ht[j]!=k
}
template <class E,class K>
int HashTable<E,K>::Search(const K &k) {
int b=hSearch(k);
if(empty[b]||ht[b]!=k) //b表示可插入的空位置索引或者起始桶时
return -1;
//e=ht[b]; //如果确实找到了key=k的元素
else return b;
}
在哈希表中插入元素
template <class E,class K>
int HashTable<E,K>::Insert(const E &e) {
K k=e;
int b=hSearch(k); //b的值可能->空桶 也可能->找到一样的元素已存在的位置 也可能是起始桶
if(empty[b]){ //空桶的情况
empty[b]=false;
ht[b]=e;
return b;
}
if(!empty[b]&&ht[b]==k){//返回元素存在的位置
return -1;
}
else if(!empty[b]&&ht[b]!=k){ //返回的是起始桶
for(;!empty[b];b=(b+1)%D); //不断往后找空桶
empty[b]=false;
ht[b]=e;
return b;
}
}
最难实现的一个删除函数 博主写了两种实现 前一种对于特定数据时间复杂度比较高 但比较容易理解
第二种的时间复杂度低 比较难理解
template <class E,class K>
int HashTable<E,K>::Delete(const K &k) {
int b = hSearch(k);
if (empty[b] || ht[b] != k) { //如果找到的是空或者回到起始桶,那么 Not Found 没找到
return -1;
} else if (ht[b] == k) { //找到想要的元素 ,删除
ht[b] = 0; //虚拟置空,只用来判断程序运行过程
empty[b] = true; //置空
int swapTimes = 0;
int after = (b + 1) % D;
for (; !empty[after]; after = (after + 1) % D) { //向删除元素节点的后续节点开始遍历
K tmp = ht[after]; //用tmp去记录被删除元素后一位元素的值
int tf = hSearch(tmp); //tf为后一位元素在散列为空时应该在的位置
if (empty[tf]) {
empty[after] = true;
empty[tf] = false;
ht[tf] = tmp;
++swapTimes;
}
}
return swapTimes;
} //O(n^2)的做法
template <class E,class K>
int HashTable<E,K>::Delete(const K &k) {
int b=hSearch(k); //找到想要删除的元素所在的位置
if(empty[b]||ht[b]!=k)
return -1; //没有找到
else if(ht[b]==k){ //正好找到了元素k
empty[b]=true; //显性置空
int swapTimes=0;
int move=b; //元素k实际所在的位置索引--move
int index=b;
int a;
do{
move=(move+1)%D; //向后遍历
if(empty[move]) //遇到空值,那么跳出循环
break;
a=ht[move]%D; //a表示x理论上所在的位置的索引
if((a<=index&&index<move)||(move<a&&a<=index)||(index<move&&move<a)){
empty[index]=false;
empty[move]=true;
ht[index]=ht[move];
index=move;
swapTimes++;
}
//其他情况不用移动
}while(!empty[(move+1)%D]&&move!=b);
return swapTimes;
}
} //O(n)做法
第一种通过代码比较容易解释 不再解释
第二种方法 要想明白 删除元素k之后 后续的元素需要如何进行移位和重排 有多少种情况
记传入元素k的实际索引为move ,即插入的元素经过碰撞后在数组中的索引
记a表示删除元素后的一个特定元素x当散列为空时 理论上拥有的数组索引
main()函数最后实现
int main() {
int D,m;
cin>>D>>m;
HashTable<int,int> T(D);
for(int i=0;i<m;i++){
int opt,x;
cin>>opt>>x;
switch(opt)
{
case 0:
int goal;
goal=T.Insert(x);
if(goal == -1)
cout<<"Existed"<<endl;
else
cout<<goal<<endl;
break;
case 1:
int element;
element=T.Search(x);
cout<<element<<endl;
break;
case 2:
int times;
times=T.Delete(x);
if(times==-1)
cout<<"Not Found"<<endl;
else
cout<<times<<endl;
break;
}
}
}