欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

哈希表开散列法(拉链法)

程序员文章站 2022-04-16 10:24:18
开散列法又叫链地址法(开链法)。 开散列法:首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。 设元素的关键码为37, 25, 14, 36, 49, 68, 57, 11, 散列表 ......

开散列法又叫链地址法(开链法)。
开散列法:首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。

设元素的关键码为37, 25, 14, 36, 49, 68, 57, 11, 散列表为HT[12],表的大小为12,散列函数为Hash(x) = x % 11
Hash(37)=4
Hash(25)=3
Hash(14)=3
Hash(36)=3
Hash(49)=5
Hash(68)=2
Hash(57)=2
Hash(11)=0
使用哈希函数计算出每个元素所在的桶号,同一个桶的链表中存放哈希冲突的元素。
哈希表开散列法(拉链法)
通常,每个桶对应的链表结点都很少,将n个关键码通过某一个散列函数,存放到散列表中的m个桶中,那么每一个桶中链表的平均长度为。以搜索平均长度为的链表代替了搜索长度为 n 的顺序表,搜索效率快的多。
应用链地址法处理溢出,需要增设链接指针,似乎增加了存储开销。事实上:
由于开地址法必须保持大量的空闲空间以确保搜索效率,如二次探查法要求装载因子a <= 0.7,而表项所占空间又比指针大的多,所以使用链地址法反而比开地址法节省存储空间。

哈希拉链相关代码如下:

使用素数表对齐做哈希表的容量,降低哈希冲突。

size_t HashTableKPrime(size_t N) //获取素数
{
  int i = 0;
  const int _PrimeSize = 28;
  static const unsigned long _PrimeList [] =
  {
      53ul, 97ul, 193ul, 389ul, 769ul,
      1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
      49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
      1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
      50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
      1610612741ul, 3221225473ul, 4294967291ul
  };
  for (i=0; i<_PrimeSize; i++)
  {
      if (_PrimeList[i] > N)
          return _PrimeList[i];
  }

  return _PrimeList[_PrimeSize-1];
}

开辟拉链的链式节点

HashNode* BuyHashKNode(KeyType key,ValueType value) //开辟新节点
{
  HashNode *tmp = (HashNode *)malloc(sizeof(HashNode));
  assert(tmp);
  tmp->_key = key;
  tmp->_value = value;
  tmp->_next = NULL;
  return tmp;
}

哈希函数

KeyType HashKFunc(KeyType key,size_t n)
{
    return key%n;
}

哈希拉链表的初始化

void HashTableKInit(HashTable *ht,size_t N)//初始化
{
  assert(ht);
  ht->_N = N;
  ht->_size = 0;
  **这里要特别注意开辟空间是给指针数组**
  ht->_tables = (HashNode **)malloc(sizeof(HashNode*)*ht->_N);
  assert(ht->_tables);
  memset(ht->_tables,0,sizeof(HashNode*)*ht->_N);
}

插入时扩容这块很关键,要重新开辟一块数组空间,把原先的表中数据映射过来,但是拉链节点不用重新开辟,直接把原先的节点拿过来。

int HashTableKInsert(HashTable* ht, KeyType key, ValueType value) //插入
{
    KeyType index;
    HashNode *node = BuyHashKNode(key,value);
    assert(ht);

    if (ht->_N == ht->_size) //扩容
    {
     size_t i = 0;
     HashTable newht;
     HashTableKInit(&newht,HashTableKPrime(ht->_N));
     for (i=0; i<ht->_N; i++)
     {
         if (ht->_tables[i])
         {
             KeyType index;
             HashNode *cur = ht->_tables[i];
             while (cur)
             {
                 HashNode *next = cur->_next;
                 index = HashKFunc(cur->_key,newht._N);

                 cur->_next = newht._tables[index];
                 newht._tables[index] = cur;

                 cur = next;
             }
         }
     }
     free(ht->_tables);
     ht->_tables = newht._tables;
     ht->_N = newht._N;
    }

    index = HashKFunc(key,ht->_N);
    if (ht->_tables[index])
    {
        HashNode *cur = ht->_tables[index];
        while (cur)
        {
            if (cur->_key == key)
                return -1;
            cur = cur->_next;
        }
    }

    node->_next = ht->_tables[index];
    ht->_tables[index] = node;
    ht->_size++;
    return 0;
}

查找函数

HashNode* HashTableKFind(HashTable* ht, KeyType key) //查找
{
    ValueType index = HashKFunc(key,ht->_N);
    assert(ht);

    if (ht->_tables[index])
    {
        if (ht->_tables[index]->_key == key)
            return ht->_tables[index];
        else
        {
            HashNode *cur = ht->_tables[index];
            while (cur)
            {
                if (cur->_key == key)
                    return cur;
                cur = cur->_next;
            }
            return NULL;
        }
    }
    else
        return NULL;
    
}

删除函数

int HashTableKRemove(HashTable* ht, KeyType key) //删除
{
    KeyType index = HashKFunc(key,ht->_N);
    assert(ht);
    
    if (ht->_tables[index])
    {
        HashNode *prev = ht->_tables[index];
        HashNode *cur = ht->_tables[index];
        while (cur)
        {
            if (cur == prev && cur->_key == key)
            {
                ht->_tables[index] = cur->_next;
                free(cur);
                cur = NULL;
                ht->_size--;
                return 0;
            }
            else if(cur->_key == key)
            {
                prev-> = cur->_next;
                free(cur);
                cur = NULL;
                ht->_size--;
                return 0;
            }

            prev = cur;
            cur = cur->_next;
        }
        return -1;
    }
    else
        return -1;
}
void HashTableKDestory(HashTable* ht) //销毁
{
    size_t i = 0;
    assert(ht);
    for (i=0; i<ht->_N;++i)
    {
        if (ht->_tables[i])
        {
            HashNode *cur = ht->_tables[i];
            while (cur)
            {
                HashNode *tmp = cur;
                cur = cur->_next;
                free(tmp);
                tmp = NULL;
            }
        }
    }
    free(ht->_tables);
    ht->_tables = NULL;
    ht->_size = 0;
    ht->_N = 0;
}

测试函数

void TestHashTableK()
{
    HashTable ht;
    HashTableKInit(&ht,3);

    HashTableKInsert(&ht,10,0);
    HashTableKInsert(&ht,11,0);
    HashTableKInsert(&ht,12,0);
    HashTableKInsert(&ht,106,0);
    HashTableKInsert(&ht,53,0);
    HashTableKInsert(&ht,1,0);
    HashTableKInsert(&ht,15,0);
    HashTableKInsert(&ht,0,0);
    HashTableKInsert(&ht,53,0);
    HashTableKInsert(&ht,52,0);
    HashTableKInsert(&ht,104,0);
    HashTableKInsert(&ht,2,0);
    HashTableKInsert(&ht,54,0);
    HashTableKInsert(&ht,108,0);
    HashTableKPrint(&ht);
    printf("\n");


    printf("%d ",HashTableKFind(&ht,2)->_key);
    printf("%d ",HashTableKFind(&ht,53)->_key);
    printf("%d ",HashTableKFind(&ht,0)->_key);
    printf("%d ",HashTableKFind(&ht,12)->_key);
    printf("%p ",HashTableKFind(&ht,156));
    printf("\n\n");


    HashTableKRemove(&ht,2);
    HashTableKRemove(&ht,53);
    HashTableKRemove(&ht,1);
    HashTableKRemove(&ht,54);
    HashTableKRemove(&ht,89);
    HashTableKPrint(&ht);

    HashTableKDestory(&ht);
    HashTableKPrint(&ht);


}

测试结果:
哈希表开散列法(拉链法)

相关哈希表概念请看哈希表详解:这里写链接内容