如何为哈希表实现擦除功能?

时间:2013-12-14 00:12:54

标签: c++ hashtable erase hash-collision

我有一个使用线性探测的哈希表。我被赋予了编写erase(int key)函数的任务,其中包含以下指南。

 void erase(int key);

 Preconditions:  key >= 0
 Postconditions: If a record with the specified key exists in the table, then
 that record has been removed; otherwise the table is unchanged.

我也得到了一些完成任务的提示

  • 重要的是要意识到插入功能允许您向表中添加新条目,或更新表中的现有条目。

  • 对于线性探测版本,请注意要插入的代码 item有两个搜索。 insert()函数调用函数 findIndex()搜索表以查看该项是否已在该表中 表。如果该项目不在表格中,则进行第二次搜索 找到表中的位置以插入项目。添加能力 删除条目将要求插入过程 改性。搜索现有项目时,请确保该项目 当涉及到被占用的位置时,搜索不会停止 但现在是空的,因为该项目已被删除。搜索时 插入新项目的位置,使用第一个空位 - 它确实如此 无论职位是否被占用都无所谓。

所以我开始写擦除(键),我似乎遇到了提示所指的问题,但我不是肯定的意思。我将在一秒钟内提供代码,但是我为测试我的代码而做的是设置哈希表,以便它会发生冲突,然后我擦除该键并重新表格,但它不会进入正确的位置。

例如,我在哈希表中添加了一些元素:

The hash table is:
Index  Key    Data
    0   31     3100
    1    1     100
    2    2     200
    3   -1
    4   -1
    5   -1
    6   -1
    7   -1
    8   -1
    9   -1
   10   -1
   11   -1
   12   -1
   13   -1
   14   -1
   15   -1
   16   -1
   17   -1
   18   -1
   19   -1
   20   -1
   21   -1
   22   -1
   23   -1
   24   -1
   25   -1
   26   -1
   27   -1
   28   -1
   29   -1
   30   -1

所以我的所有值都是空的,除了前3个索引。显然,密钥31应该进入索引1.但是由于密钥1已经存在,它会与索引0发生碰撞和结算。然后擦除密钥1并重新运行表,但密钥31保持在索引0。

以下是可能值得关注的功能:

void Table::insert( const RecordType& entry )
{
   bool alreadyThere;
   int index;

   assert( entry.key >= 0 );

   findIndex( entry.key, alreadyThere, index );
   if( alreadyThere )
      table[index] = entry;   
   else
   {
      assert( size( ) < CAPACITY );
      index = hash( entry.key );
      while ( table[index].key != -1 )
         index = ( index + 1 ) % CAPACITY;
      table[index] = entry;
      used++;
   }
}

由于insert使用findIndex,我也会包含它

void Table::findIndex( int key, bool& found, int& i ) const
{
   int count = 0;

   assert( key >=0 );

   i = hash( key );
   while ( count < CAPACITY && table[i].key != -1 && table[i].key != key )
   {
      count++;
      i = (i + 1) % CAPACITY;
   }   
   found = table[i].key == key;
}

这是我目前的擦除开始

void Table::erase(int key) 
{
    assert(key >= 0);

    bool found, rehashFound;
    int index, rehashIndex;

    //check if key is in table
    findIndex(key, found, index);

    //if key is found, remove it
    if(found)
    {
        //remove key at position
        table[index].key = -1;
        table[index].data = NULL;
        cout << "Found key and removed it" << endl;
        //reduce the number of used keys
        used--;
        //rehash the table

        for(int i = 0; i < CAPACITY; i++)
        {
            if(table[i].key != -1)
            {
                cout << "Rehashing key : " << table[i].key << endl;
                findIndex(table[i].key, rehashFound, rehashIndex);
                cout << "Rehashed to index : " << rehashIndex << endl;
                table[rehashIndex].key = table[i].key;
                table[rehashIndex].data = table[i].data;
            }
        }
    }
}

有人可以解释一下我需要做些什么才能让它正常重组?我理解哈希表的概念,但我似乎在这里做错了。

修改

根据用户的建议:

void Table::erase(int key)
{
    assert(key >= 0);
    bool found;
    int index;

    findIndex(key, found, index);

    if(found) 
    {
        table[index].key = -2;
        table[index].data = NULL;
        used--;

    }

}


//modify insert(const RecordType & entry)

while(table[index].key != -1 || table[index].key != -2)


//modify findIndex

while(count < CAPACITY && table[i].key != -1
      && table[i].key != -2 && table[i].key != key)

2 个答案:

答案 0 :(得分:2)

从表格中删除项目时,请勿移动任何物品。只需在那里粘贴“已删除”标记即可。在插入上,将删除标记视为空并可用于新项目。在查找时,将它们视为已占用,并在遇到一个时继续探测。调整表格大小时,请忽略标记。

请注意,如果从未调整表的大小,这可能会导致问题。如果从未调整表的大小,一段时间后,您的表将没有标记为从未使用过的条目,并且查找性能将下降到地狱。由于提示提醒我们是否曾经使用空位并且曾经使用过的单元与未使用过的单元不同,我相信这是预期的解决方案。据推测,调整表的大小将是后来的任务。

答案 1 :(得分:1)

每次删除都没有必要重新整理整个表。如果你想最大限度地降低性能下降,那么你可以通过考虑删除后的元素之后但是在删除之前或之前的下一个-1哈希到桶之前的任何元素(通过从头到尾包装允许)来压缩表element - 如果是这样,那么它们可以移动到或至少更靠近它们的哈希桶,然后你可以重复刚刚移动的元素的压缩过程。

执行此类压缩将消除当前代码中的最大缺陷,即在稍微使用后,每个存储桶将被标记为正在使用或已被使用,以及例如查找不存在的值将降级为O(CAPACITY)。

没有编译器/测试就脱离了我的脑海......

int Table::next(int index) const
{
    return (index + 1) % CAPACITY;
}

int Table::distance(int from, int to) const
{
    return from < to ? to - from : to + CAPACITY - from;
}

void Table::erase(int key)
{
    assert(key >= 0);
    bool found;
    int index;

    findIndex(key, found, index);

    if (found) 
    {
        // compaction...
        int limit = CAPACITY - 1;
        for (int compact_from = next(index);
             limit-- && table[compact_from].key >= 0;
             compact_from = next(compact_from))
        {
            int ideal = hash(table[compact_from].key);
            if (distance(ideal, index) <
                distance(ideal, compact_from))
            {
                table[index] = table[compact_from];
                index = compact_from;
            }
        }

        // deletion
        table[index].key = -1;
        delete table[index].data; // or your = NULL if not a leak? ;-.
        --used;
    }
}