散列之开放地址法

开放定址散列法

  • 分离链表法缺点:需要指针,给新单元分配地址需要时间,导致算法速度减慢,同时还需要实现另一种数据结构。
  • 开放定址法:如果有冲突发生,尝试选择另外得单元,直到找出空的单元为止。

开放定址散列法数据结构

  • 散列表单元不是链表,而是一个个散列项:包含关键字和相关信息(是否为空),数据结构声明如下:
typedef struct Entry {      // 定义哈希表元素的结构
    ElemType data;
    KindOfEntry Info;
}HashEntry;
  • 散列表的数据结构包含:表大小,一个散列项类型的数组(头指针),结构声明如下
typedef struct HashTb {     // 定义哈希表的结构
    int tablesize;          // 哈希表的大小
    HashEntry* TableList;   // 定义一个元素为HashEntry的数组,头指针为Tablelist
}*HashTable;

使用平方探测法散列法查找散列表

  • 平方探测法是开放定址散列法的一种。
Index Find(HashTable H, ElemType key)       // 使用平方探测散列法,在哈希表中查找指定关键字,返回一个位置下标
{
    Index pos  = Hash(key, H->tablesize);       // 借助哈希函数,求出包含给定关键字的位置
    int collnum = 0;        // 初始化冲突数
    while (H->TableList[pos].Info != Empty && H->TableList[pos].data != key)
    {
        pos += 2 * ++collnum - 1;
        if (pos >= H->tablesize)
            pos -= H->tablesize;
    }
    return pos;
}

使用平方探测法散列表的插入操作

  • 定理:使用平方探测法,如果表大小是素数,则当表至少有一半是空的时候,总能插入一个新的元素
void insert(HashTable H, ElemType key)          // 向哈希表中插入元素
{
    Index pos = Find(H, key);               // 先在表中确定key是否存在,即哈希项的信息是否为Legitimate表示已被装填了
    if (H->TableList[pos].Info != Legitimate)               
    {
        H->TableList[pos].data = key;           // 向表中插入数据
        H->TableList[pos].Info = Legitimate;    // 将哈希项的info置为Legitimate
    }
}

再散列

  • 对于平方探测的开放定址散列法,如果表的元素填得太满,操作运行时间将开始消耗过长,且insert可能失败。
  • 一种解决方法是建立大约两倍大的表(使用一个相关的新散列函数),扫描原散列表,计算表中元素新散列值将它们插入到新表中。这种操作称为再散列。
HashTable Rehash(HashTable H)       // 对开放定址散列表再散列
{
    int oldtablesize = H->tablesize;    // 备份哈希表的大小,及存放数据的列表
    HashEntry *oldTableList = H->TableList;

    H = InitHashTable(2 * oldtablesize);    // 扩充容量为原来的两倍
    for (int i = 0;i < oldtablesize;i++)    // 将原哈希表中存在的元素移到新哈希表中
    {
        if (oldTableList[i].Info == Legitimate)
            insert(H, oldTableList[i].data);
    }
    delete oldTableList;        // 释放原哈希表中的元素
    return H;
}

附平方探测开放定址散列表及相关操作C/C++

#include<iostream>
#define Index int

using namespace std;
typedef int ElemType;
enum KindOfEntry {Empty, Legitimate};
const int Mintablesize = 5;

typedef struct Entry {      // 定义哈希表元素的结构
    ElemType data;
    KindOfEntry Info;
}HashEntry;

typedef struct HashTb {     // 定义哈希表的结构
    int tablesize;          // 哈希表的大小
    HashEntry* TableList;   // 定义一个元素为HashEntry的数组,头指针为Tablelist
}*HashTable;

bool isPrime(int x)         // 判断一个数是否为复数
{
    if (x == 1)
        return false;
    if (x == 2)
        return true;
    for (int i = 3;i < x / 2;i++)
    {
        if (x%i == 0)
            return false;
    }
    return true;
}

int NextPrime(int x)        // 求大于x的下一个素数
{
    while (1)
    {
        x++;
        if (isPrime(x))
            return x;
    }
}

int Hash(ElemType key, int tablesize)
{
    return key % tablesize;
}

HashTable InitHashTable(int tablesize)      // 初始化大小为tablesize的哈希表
{
    if (tablesize < Mintablesize)
    {
        cout << "Table size too small.\n";
        exit(1);
    }
    HashTable H = new HashTb;

    H->tablesize = NextPrime(tablesize);    // 表的大小为给定大小的下一个素数

    H->TableList = new HashEntry [tablesize];       // 为H的TableList数组分配tablesize个HashEntry的空间

    for (int i = 0;i < H->tablesize;i++)
    {
        H->TableList[i].Info = Empty;           // 初始化每个HashEntry的info为空
    }
    return H;
}

Index Find(HashTable H, ElemType key)       // 使用平方探测散列法,在哈希表中查找指定关键字,返回一个位置下标
{
    Index pos  = Hash(key, H->tablesize);       // 借助哈希函数,求出包含给定关键字的位置
    int collnum = 0;        // 初始化冲突数
    while (H->TableList[pos].Info != Empty && H->TableList[pos].data != key)
    {
        pos += 2 * ++collnum - 1;
        if (pos >= H->tablesize)
            pos -= H->tablesize;
    }
    return pos;
}

void insert(HashTable H, ElemType key)          // 向哈希表中插入元素
{
    Index pos = Find(H, key);               // 先在表中确定key是否存在,即哈希项的信息是否为Legitimate表示已被装填了
    if (H->TableList[pos].Info != Legitimate)               
    {
        H->TableList[pos].data = key;           // 向表中插入数据
        H->TableList[pos].Info = Legitimate;    // 将哈希项的info置为Legitimate
    }
}

HashTable Rehash(HashTable H)       // 对开放定址散列表再散列
{
    int oldtablesize = H->tablesize;    // 备份哈希表的大小,及存放数据的列表
    HashEntry *oldTableList = H->TableList;

    H = InitHashTable(2 * oldtablesize);    // 扩充容量为原来的两倍
    for (int i = 0;i < oldtablesize;i++)    // 将原哈希表中存在的元素移到新哈希表中
    {
        if (oldTableList[i].Info == Legitimate)
            insert(H, oldTableList[i].data);
    }
    delete oldTableList;        // 释放原哈希表中的元素
    return H;
}

int main()
{
    const int rawdata[] = { 19, 13, 9, 8, 23, 39, 4, 2, 75, 100, 43, 58 };
    int tablesize = 12;

    HashTable myTable = InitHashTable(tablesize);

    for (int i = 0;i < sizeof(rawdata) / sizeof(int);i++)
    {
        insert(myTable, rawdata[i]);        // 向表中插入给定数据
    }

    cout << "Find the table : \n";
    for (int i = 0;i < sizeof(rawdata) / sizeof(int);i++)
    {
        if (myTable->TableList[Find(myTable, rawdata[i])].Info == Legitimate)
        {
            cout << rawdata[i] << " Have been Found." << endl;
        }
        else
        {
            cout << rawdata[i] << " 404 Not Found." << endl;
        }

    }
    int key;
    cout << "Input an int number: "; cin >> key;
    Index pos = Find(myTable, key);
    if (myTable->TableList[pos].Info == Empty)
        cout << key << " is not in the table." << endl;

    delete myTable;
    system("pause");
    return 0;
}
  • 操作运行结果
Find the table :
19 Have been Found.
13 Have been Found.
9 Have been Found.
8 Have been Found.
23 Have been Found.
39 Have been Found.
4 Have been Found.
2 Have been Found.
75 Have been Found.
100 Have been Found.
43 Have been Found.
58 Have been Found.
Input an int number: 15
15 is not in the table.
请按任意键继续. . .

猜你喜欢

转载自blog.csdn.net/weixin_40170902/article/details/80747269