通过线性探测法构建的散列表的删除实现

在最近对散列表的学习中,以线性探测法处理的散列表的删除操作让我无所适从,从网上、考研参考书等等各方面查找到的资料也都不尽人意,因此写写博客,希望能够给同样无所适从的朋友一点思路。

我主要针对机械工业出版社的《数据结构、算法与应用 C++描述(第二版)》这本资料来具体阐述。

首先是线性探测法的描述特点:当在哈希表中插入某个数时,如果冲突发生的时候,他会以循环表的方式顺序查看表中下一个单元,直到找出一个空闲单元或查遍全表。

这样线性探测法可能使第 i 个散列地址的同义词存入第 i+1个散列地址,这样本应该存入第 i+1个散列地址的元素就会争夺第 i+2个散列地址的元素的地址。从而造成大量元素在相邻的散列地址上聚集起来,大大降低了查找效率。同时,也给删除操作带来了比较大的困难。

文章后面我会附上代码,我看了看代码占了好多啊!!还是看erase吧

对于删除操作。我们首先搜索删除的数的位置,然后从删除的位置的下一个桶开始,逐个检查每个桶,以确定要移动的元素,直至到达一个空桶或者回到删除位置位置。在删除操作时,一定要注意,不要把一个数对移到它的起始桶之前,否则,对这个书对的查找就可能失败。(这是书上原句,这个”可能“用得十分好)

    我是通过定点a与b操作来实现,b逐次增加依次往后遍历负责判断该值是否应该前移,a负责将应该前移的数往前移动。

hash(theKey)=theKey; D除数=11;

012345678910
0112625150171828 3921

这个操作主要要分为四种情况:

我是通过定点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;

}

 

  • 8
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
线性探测再散列法是一种处理冲突的方法。当发生冲突时,它会从发生冲突的地址开始,依次探查下一个地址,直至找到一个空位置为止。如果在查找失败时,还没有找到需要查找的元素,那么可以根据线性探测法的思路,继续向后查找直到找到一个空位置或者遍历完整个散列表。这样可以实现查找不成功的功能。 参考中提到了线性探测法的处理冲突方式,它可以用来解决散列表中的冲突问题。当然,线性探测法也有一定的局限性,例如极易产生堆积问题。因此,在选择处理冲突的方法时,需要根据具体问题的特点来决定是否采用线性探测法。 参考中给出了一个具体的例子,展示了如何通过线性探测法构建散列表,并计算查找失败的平均长度和查找成功的平均长度。通过这个例子,可以更好地理解线性探测法的应用和计算方法。 参考中的题目要求使用线性探测法将给定关键字序列散列存储到散列表中,并计算等概率情况下的查找成功和不成功的平均查找长度。可以根据散列函数和线性探测法的规则来构造散列表,并根据相关公式计算平均查找长度。 综上所述,线性探测再散列法可以用于处理查找不成功的情况,并且可以根据具体问题的要求来设计散列表和计算平均查找长度。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [计算散列表查找成功和查找不成功的平均查找长度(利用线性探测法处理冲突)](https://blog.csdn.net/weixin_43305485/article/details/120680371)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值