在最近对散列表的学习中,以线性探测法处理的散列表的删除操作让我无所适从,从网上、考研参考书等等各方面查找到的资料也都不尽人意,因此写写博客,希望能够给同样无所适从的朋友一点思路。
我主要针对机械工业出版社的《数据结构、算法与应用 C++描述(第二版)》这本资料来具体阐述。
首先是线性探测法的描述特点:当在哈希表中插入某个数时,如果冲突发生的时候,他会以循环表的方式顺序查看表中下一个单元,直到找出一个空闲单元或查遍全表。
这样线性探测法可能使第 i 个散列地址的同义词存入第 i+1个散列地址,这样本应该存入第 i+1个散列地址的元素就会争夺第 i+2个散列地址的元素的地址。从而造成大量元素在相邻的散列地址上聚集起来,大大降低了查找效率。同时,也给删除操作带来了比较大的困难。
文章后面我会附上代码,我看了看代码占了好多啊!!还是看erase吧
对于删除操作。我们首先搜索删除的数的位置,然后从删除的位置的下一个桶开始,逐个检查每个桶,以确定要移动的元素,直至到达一个空桶或者回到删除位置位置。在删除操作时,一定要注意,不要把一个数对移到它的起始桶之前,否则,对这个书对的查找就可能失败。(这是书上原句,这个”可能“用得十分好)
我是通过定点a与b操作来实现,b逐次增加依次往后遍历负责判断该值是否应该前移,a负责将应该前移的数往前移动。
hash(theKey)=theKey; D除数=11;
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
0 | 1 | 12 | 62 | 51 | 50 | 17 | 18 | 28 | 39 | 21 |
这个操作主要要分为四种情况:
我是通过定点a与b操作来实现,b逐次增加依次往后遍历负责判断该值是否应该前移,a负责将应该前移的数往前移动。
设初始删除的桶为 n=b=a=search(theKey) //这里写得粗糙了点 详情可看代码
1)a,b均大于n,并且前移的数的起始桶数也大于n;
在第一种情况中,又分有两种情况,“不要把一个数对移到它的起始桶之前”这句话在这两种情况中分别为对和错;
即删除17,28和39数对往前移动的情况,此时这句话是对的;
如果删除1,这时候数对12、 51、 50都需要往前移,这时候如果一个数对已经在起始桶之前了,那么可以将其往前移。
2)a大于n,b小于n,前移的的数的起始桶数大于n;
即删除17,51数对向前移动得情况,注意!!这时候50也要往前移动
3)a,b均小于n,并且前移的数的起始桶的位置大于n;
对应删除18,51需要往前移动得情况。
就到这里,对应这三种大情况:(a<b&&m>b)||(a>b&&m>b&&a>=m)||(a<b&&b>m&&a>=m) 这几个逻辑表达式就可以完全表示出来,//详情看代码得实现环境。
我写得这个没有经过大量数据得验证,只有将近50多条数据,可能有的情况没有考虑到,因此我只是提供了一个思路。
另外,以下代码参考以上书中资料,对了,我认为search函数是整个代码得精髓之所在,详参详悟后得体会不一般!
#include <iostream>
using namespace std;
class hashTableFull
{
public:
hashTableFull(string theMessage =
"The hash table is full")
{message = theMessage;}
void outputMessage() {cout << message << endl;}
private:
string message;
};
template <class K> class hash;
template<>
class hash<int>
{
public:
size_t operator()(const int theKey) const
{return size_t(theKey);}
};
template<class K, class E>
class hashTable
{
public:
hashTable(int theDivisor = 971);
~hashTable(){delete [] table;}
bool empty() const {return dSize == 0;}
int size() const {return dSize;}
pair<const K, E>* find(const K&) const;
void insert(const pair<const K, E>&);
void output(ostream& out) const;
void erase(const K theKey);
protected:
int search(const K&) const;
pair<const K, E>** table; // hash table
hash<K> hash; // maps type K to nonnegative integer
int dSize; // number of pairs in dictionary
int divisor; // hash function divisor
};
template<class K, class E>
hashTable<K,E>::hashTable(int theDivisor)
{
divisor = theDivisor;
dSize = 0;
// allocate and initialize hash table array
table = new pair<const K, E>* [divisor];
for (int i = 0; i < divisor; i++)
table[i] = NULL;
}
template<class K, class E>
int hashTable<K,E>::search(const K& theKey) const
{// Search an open addressed hash table for a pair with key theKey.
// Return location of matching pair if found, otherwise return
// location where a pair with key theKey may be inserted
// provided the hash table is not full.
int i = (int) hash(theKey) % divisor; // home bucket
int j = i; // start at home bucket
do
{
if (table[j] == NULL || table[j]->first == theKey)
return j;
j = (j + 1) % divisor; // next bucket
} while (j != i); // returned to home bucket?
return j; // table full
}
template<class K, class E>
pair<const K,E>* hashTable<K,E>::find(const K& theKey) const
{// Return pointer to matching pair.
// Return NULL if no matching pair.
// search the table
int b = search(theKey);
// see if a match was found at table[b]
int count=-1;
if (table[b] == NULL || table[b]->first != theKey)
{
cout<<count<<endl;
pair<const K,E>* thepair=NULL;
return thepair; // no match
}
cout<<b<<endl;
return table[b]; // matching pair
}
template<class K, class E>
void hashTable<K,E>::insert(const pair<const K, E>& thePair)
{// Insert thePair into the dictionary. Overwrite existing
// pair, if any, with same key.
// Throw hashTableFull exception in case table is full.
// search the table for a matching pair
int b = search(thePair.first);
// check if matching pair found
if (table[b] == NULL)
{
// no matching pair and table not full
table[b] = new pair<const K,E> (thePair);
dSize++;
cout<<b<<endl;
}
else
{// check if duplicate or table full
if (table[b]->first == thePair.first)
{// duplicate, change table[b]->second
cout<<"Existed"<<endl;
table[b]->second = thePair.second;
}
else // table is full
throw hashTableFull();
}
}
template<class K,class E>
void hashTable<K,E>::erase(const K theKey)
{//表中元素的删除 create erase function
int count=0;
int b=search(theKey);//find the delete bucket number
int a=b,n=b;//b为判断合适与否 a为对b中符合要求的前移 n用来定位
//if b cut the mustard then use a to put it forward ;else use n to diecison
pair<const K,E>* current=table[b];
if(table[b]==NULL||table[b]->first!=theKey)
cout<<"Not Found"<<endl;
else
{ delete current;//删除这个 delete current
b=(b+1)%divisor;//b首先加一移位
while(table[b]!=NULL&&b!=n)//只有走到null或者这个表走完了方才停止
{
int m=(int)table[b]->first%divisor;
if((a<b&&m>b)||(a>b&&m>b&&a>=m)||(a<b&&b>m&&a>=m)) //the three logical relations will be explain in the essay
{
table[a]=table[b];//exchange a and b
a=b;
count++;//exchange time
}
b=(b+1)%divisor;//b要一个个往后探查 let b to point next bucket
}
cout<<count<<endl;
}
table[a]=NULL;//对最后一个没有值的赋值为null;
}
template<class K, class E>
void hashTable<K,E>::output(ostream& out) const
{// Insert the hash table into the stream out.
for (int i = 0; i < divisor; i++)
if (table[i] == NULL)
cout << "NULL" << endl;
else
cout << table[i]->first << " "
<< table[i]->second << endl;
}
// overload <<
template <class K, class E>
ostream& operator<<(ostream& out, const hashTable<K,E>& x)
{x.output(out); return out;}
int main()
{//可以用第二个注释进行调试
int D,m;
cin>>D>>m;
hashTable<int, int> z(D);
pair<int, int> p;
int op,x;
while(m--)
{
cin>>op>>x;
if(op==0)
{
p.first=x;p.second =m;
z.insert(p);
}
else
{
if(op==1)
z.find(x);
else
z.erase(x);
}
}
/* hashTable<int, int> z(11);
pair<int, int> p;
p.first=17; p.second =6;
z.insert(p);
p.first=18; p.second =7;
z.insert(p);
p.first=28; p.second =8;
z.insert(p);
p.first=39; p.second =9;
z.insert(p);
p.first=21; p.second =10;
z.insert(p);
p.first=0; p.second =0;
z.insert(p);
p.first=1; p.second =1;
z.insert(p);
p.first=12; p.second =2;
z.insert(p);
p.first=62; p.second =3;
z.insert(p);
p.first=51; p.second =4;
z.insert(p);
p.first=50; p.second =5;
z.insert(p);
//z.erase(39);
// z.erase(62);
cout<<"test"<<endl<<z<<endl; */
return 0;
}