雜湊表 ADT 開放地址法解決衝突【資料結構與演算法分析 c 語言描述】

jerrkill發表於2019-01-25

前一節用分離連結法解決了衝突問題,同時該演算法也具有自己的缺點。

  • 需要指標,新單元分配地址需要時間,導致速度減慢。
  • 需要實現另一種資料結構(單連結串列)。

所以就有了開放地址法,如其名 開放地址 就是把其他地址也開放出來 比如在 分離連結法裡面 hash 值為 0 的單元只對映 hash(x) = 0 的關鍵字,而在開放地址法裡面就會把這些地址開放給其他的關鍵字,開放地址法:當有衝突時,就嘗試選擇另外的單元,直到找出空單元為止 那麼他們又通過什麼規則來確定對映關係以及尋找另外的單元的呢?這就是下面要說的 線性探測平方探測

線性探測
顧名思義,就是一個接一個的尋找,比如 hash(x) = 0;在 0 單元上發生衝突,就往下探測 1、2、3 ...直到找到一個空單元為止。

平方探測
跟名字一樣,就是當衝突發生時用二次函式來作為尋找單元的計算方法,如 hash(x) = 0;在 0 單元上發生衝突,下一個位置為:F(i) = i^2 來探測,i 第幾次探測。

雜湊表的結構圖如下:
file
hash_quad.h 標頭檔案定義

typedef unsigned int index;
typedef char* element_type;
typedef index position;

struct hash_table_node;
typedef struct hash_table_node *hash_table;

index hash(element_type key, int table_size);
hash_table initialize_table(int table_size);
void destroy_table(hash_table h);
position find(element_type key, hash_table h);
void insert(element_type key, hash_table h);
element_type retrieve(position p, hash_table h);
void delete(element_type key, hash_table h);
int next_prime(int table_size);

void random_hash_table(hash_table h, int len);
void print_hash_table(hash_table h);
void test();

enum kind_of_entry { legitimate, empty, deleted };

struct hash_entry
{
    element_type element;
    enum kind_of_entry info;
};

typedef struct hash_entry cell;

struct hash_table_node
{
    int table_size;
    int size;
    cell *pcell_arr;
};

hash_quad 實現

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include "hash_quad.h"

#define error(str) fatal_error(str)
#define fatal_error(str) fprintf(stderr, "%s\n", str), exit(1)

index hash(element_type key, int table_size)
{
    unsigned int hash_value = 0;
    while (*key != '\0') {
        hash_value = (hash_value << 5) + *key++;
    }
    return hash_value % table_size;
}

int next_prime(int table_size)
{
    int i, j = 2, k;

    for (i = table_size; i > 0; i--) {
        k = sqrt(i);
        while (j <= k) {
            if (i % j == 0)
                break;
            j++;
        }

        if (j > k)
            break;
    }

    return i;
}

hash_table initialize_table(int table_size)
{
    int min_table_size = 5;
    hash_table h;
    int i;

    if ( table_size < min_table_size) {
        error(" Table size is too small");
        return NULL;
    }

    h = (hash_table)malloc(sizeof(struct hash_table_node));
    if (NULL == h)
        fatal_error("out of space");

    h->table_size = next_prime(table_size);
    h->size = 0;
    h->pcell_arr = malloc(sizeof(struct hash_entry) * h->table_size);
    if (NULL == h->pcell_arr)
        fatal_error("out of space");

    for (i = 0; i < h->table_size; i++) {
        h->pcell_arr[i].info = empty;
    }

    return h;
}

position find(element_type key, hash_table h)
{
    position current_pos;
    int i;

    i = 0;
    current_pos = hash(key, h->table_size);
    while (h->pcell_arr[current_pos].info != empty && h->pcell_arr[current_pos].element != key) {
        // 線性探測
        // current_pos += 1;
        // 平方探測
        current_pos += 2 * ++i - 1;
        if (current_pos >= h->table_size)
            current_pos -= h->table_size;
    }

    return current_pos;
}
void insert(element_type key, hash_table h)
{
    position pos;

    if (h->size > 0.8 * h->table_size) {
        printf("超出 0.8 的裝填因子,需要擴容\n");
        return;
    }

    pos = find(key, h);

    if (h->pcell_arr[pos].info != legitimate) {
        h->pcell_arr[pos].info = legitimate;
        h->pcell_arr[pos].element = key;
        h->size++;
    }

}

void destroy(hash_table h)
{
    if (NULL == h)
        error("Hash Table is NULL");

    int i;

    free(h->pcell_arr);

    free(h);
}

void delete(element_type key, hash_table h)
{
    position pos;
    pos = find(key, h);
    if (h->pcell_arr[pos].info == legitimate) {
        h->pcell_arr[pos].info = deleted;
        free(h->pcell_arr[pos].element);
        h->size--;
    }
}

void random_hash_table(hash_table h, int len)
{
    char dictionary[52] = "adcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int str_len, i, j;

    srand((unsigned)time(NULL));
    for (i = 0; i < len; i++) {
        str_len = rand() % 8 + 1; // 1-8
        char* str;
        str = (char*)malloc(sizeof(char) * (str_len + 1));
        for (j = 0; j < str_len; j++) {
            str[j] = dictionary[rand() % 52];
        }
        str[j] = '\0';
        insert(str, h);
        str = NULL;
    }
}

void print_hash_table(hash_table h)
{
    index i;

    for (i = 0; i < h->table_size; i++) {
        printf("%d\t=>", i);
        if (h->pcell_arr[i].info == legitimate)
            printf("\t%s", h->pcell_arr[i].element);
        printf("\n");
    }
}

void test()
{
    hash_table h;

    h = initialize_table(22);
    printf("\t\tinsert adc into hash table.\n");
    char* str = (char*)malloc(sizeof(char) * 4);
    str[0] = 'a';
    str[1] = 'b';
    str[2] = 'c';
    str[3] = '\0';
    insert(str, h);
    print_hash_table(h);
    printf("\t\tdelete abc.\n");
    delete(str, h);
    print_hash_table(h);
    random_hash_table(h, 13);
    printf("\t\ta random hash table\n");
    print_hash_table(h);
}

int main(int argc, char const *argv[])
{
    test();
    return 0;
}

file
file

  • 開放地址法。
  • 線性探測。
  • 平方探測。
  • 陣列結構實現的雜湊表。
  • c 的 char 型別 跟 字串的理解。

高度自律,深度思考,以勤補拙

相關文章