欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語言手撕一個Hash表(HashTable)實例代碼

 更新時間:2023年03月24日 14:15:46   作者:LyaJpunov  
哈希表(HashTable)是一種非常重要的數(shù)據(jù)結(jié)構(gòu),它可以在常量時間內(nèi)進行插入、查找和刪除操作,下面這篇文章主要給大家介紹了關(guān)于C語言手撕一個Hash表(HashTable)的相關(guān)資料,需要的朋友可以參考下

什么是Hash Table

散列表用的是數(shù)組支持按照下標隨機訪問數(shù)據(jù)的特性,所以散列表其實就是數(shù)組的一種擴展,由數(shù)組演化而來??梢哉f,如果沒有數(shù)組,就沒有散列表。

散列函數(shù)

散列函數(shù)是將我們想插入的節(jié)點散列成一個數(shù)值的函數(shù)。它是一個函數(shù)。我們可以把它定義成 hash(key),要想找到一個不同的 key 對應(yīng)的散列值都不一樣的散列函數(shù),幾乎是不可能的。即便像業(yè)界著名的MD5、SHA、CRC等哈希算法,也無法完全避免這種散列沖突。而且,因為數(shù)組的存儲空間有限,也會加大散列沖突的概率。

所以我們幾乎無法找到一個完美的無沖突的散列函數(shù),即便能找到,付出的時間成本、計算成本也是很大的,所以針對散列沖突問題,我們需要通過其他途徑來解決。

散列沖突

開放尋址法

開放尋址法的核心思想是,如果出現(xiàn)了散列沖突,我們就重新探測一個空閑位置,將其插入。那如何重新探測新的位置呢?我先講一個比較簡單的探測方法,線性探測(Linear Probing)。當(dāng)我們往散列表中插入數(shù)據(jù)時,如果某個數(shù)據(jù)經(jīng)過散列函數(shù)散列之后,存儲位置已經(jīng)被占用了,我們就從當(dāng)前位置開始,依次往后查找,看是否有空閑位置,直到找到為止。

鏈表法

鏈表法是一種更加常用的散列沖突解決辦法,相比開放尋址法,它要簡單很多。我們來看這個圖,在散列表中,每個“桶(bucket)”或者“槽(slot)”會對應(yīng)一條鏈表,所有散列值相同的元素我們都放到相同槽位對應(yīng)的鏈表中。

HashMap 底層采用鏈表法來解決沖突。即使負載因子和散列函數(shù)設(shè)計得再合理,也免不了會出現(xiàn)拉鏈過長的情況,一旦出現(xiàn)拉鏈過長,則會嚴重影響 HashMap 的性能。

于是,在 JDK1.8 版本中,為了對 HashMap 做進一步優(yōu)化,我們引入了紅黑樹。而當(dāng)鏈表長度太長(默認超過 8)時,鏈表就轉(zhuǎn)換為紅黑樹。我們可以利用紅黑樹快速增刪改查的特點,提高 HashMap 的性能。當(dāng)紅黑樹結(jié)點個數(shù)少于 8 個的時候,又會將紅黑樹轉(zhuǎn)化為鏈表。因為在數(shù)據(jù)量較小的情況下,紅黑樹要維護平衡,比起鏈表來,性能上的優(yōu)勢并不明顯。

裝載因子

裝載因子越大,說明散列表中的元素越多,空閑位置越少,散列沖突的概率就越大。不僅插入數(shù)據(jù)的過程要多次尋址或者拉很長的鏈,查找的過程也會因此變得很慢。

最大裝載因子默認是 0.75,當(dāng) HashMap 中元素個數(shù)超過 0.75*capacity(capacity 表示散列表的容量)的時候,就會啟動擴容,每次擴容都會擴容為原來的兩倍大小。

代碼

這里我們給出C語言的HashTable代碼,我們使用的是鏈表法,而且也沒有在鏈表過長的時候?qū)⑵滢D(zhuǎn)化為紅黑樹,只是單純的鏈表。

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

typedef struct Node {
    int key;
    int val;
    struct Node *next;
} Node;

typedef struct HashMap {
    int size;
    int count;
    struct Node **hashmap;
} HashMap;

// 定義哈希函數(shù)
unsigned int hash(int n) {
    return (unsigned int) n;
}

// 創(chuàng)建一個節(jié)點
Node *creatNode(int key, int val) {
    Node *node = (Node *) malloc(sizeof(Node));
    node->key = key;
    node->val = val;
    node->next = NULL;
    return node;
}

// 創(chuàng)建一個hash表
HashMap *createHashMap() {
    HashMap *H = (HashMap *) malloc(sizeof(HashMap));
    H->size = 8;
    H->count = 0;
    H->hashmap = (Node **) calloc(H->size, sizeof(Node *));
    return H;
}

// 擴容,以2倍的形式擴容
static void extend(HashMap *map) {
    int newsize = map->size * 2;
    Node **newhashmap = (Node **) calloc(newsize, sizeof(Node *));
    for (int i = 0; i < map->size; i++) {
        Node *node = map->hashmap[i];
        Node *head1 = (Node *) malloc(sizeof(Node));
        Node *head2 = (Node *) malloc(sizeof(Node));
        Node *temp1 = head1;
        Node *temp2 = head2;
        while (node) {
            if (hash(node->key) % newsize != i) {
                temp2->next = node;
                temp2 = temp2->next;
            } else {
                temp1->next = node;
                temp1 = temp1->next;
            }
            node = node->next;
        }
        temp1->next = NULL;
        temp2->next = NULL;
        newhashmap[i] = head1->next;
        newhashmap[i + map->size] = head2->next;
        free(head1);
        free(head2);
    }
    map->size = newsize;
    free(map->hashmap);
    map->hashmap = newhashmap;
}

// 插入某個結(jié)點
bool insert(HashMap *map, int key, int val) {
    int hash_key = hash(key) % map->size;
    // 要插入的哈希值未產(chǎn)生碰撞
    if (map->hashmap[hash_key] == NULL) {
        map->count++;
        map->hashmap[hash_key] = creatNode(key, val);
    } else {
        Node *head = map->hashmap[hash_key];
        if (head->key == key) return false;
        while (head->next && head->next->key != key) head = head->next;
        if (head->next == NULL) {
            map->count++;
            head->next = creatNode(key, val);
        } else if (head->next->key == key) return false;
    }
	// 裝載因子過高就開始擴容
    if (map->count / map->size > 0.75) extend(map);
    return true;
}

// 尋找某個結(jié)點
bool find(HashMap *map, int key, int *v) {
    int hash_key = hash(key) % map->size;
    if (map->hashmap[hash_key] == NULL) {
        return false;
    } else {
        Node *head = map->hashmap[hash_key];
        if (head->key == key) {
            *v = head->val;
            return true;
        }
        while (head->next && head->next->key != key) head = head->next;
        if (head->next == NULL) return false;
        if (head->next->key == key) {
            *v = head->next->val;
            return true;
        }
    }
    return false;
}

// 刪除某個結(jié)點
void delete(HashMap *map, int key) {
    int hash_key = hash(key) % map->size;
    if (map->hashmap[hash_key] == NULL) return;
    Node *head = map->hashmap[hash_key];
    if (head->key == key) {
        map->hashmap[hash_key] = head->next;
        map->count--;
        free(head);
        return;
    }
    while (head->next && head->next->key != key) head = head->next;
    if (head->next == NULL) return;
    if (head->next->key == key) {
        Node *temp = head->next;
        head->next = head->next->next;
        map->count--;
        free(temp);
    }
    return;
}


int main() {
    HashMap *H = createHashMap();
    for (int i = 0; i < 1024; i++) {
        insert(H, i, i);
    }
    printf("H size is %d\n",H->size);
    printf("H count is %d\n",H->count);
    delete(H, 0);
    int v = 0;
    if (find(H, 0, &v)) printf("%d\n", v);
    else printf("not find \n");
    if (find(H, 4, &v)) printf("%d\n", v);
    else printf("not find \n");
    return 0;
}

總結(jié)

到此這篇關(guān)于C語言手撕一個Hash表(HashTable)的文章就介紹到這了,更多相關(guān)C語言Hash表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C/C++中提高查找速度的小技巧

    C/C++中提高查找速度的小技巧

    這篇文章主要給大家介紹了C/C++中提高數(shù)組中查找某個元素或者字符串中查找某個字符效率的小技巧,提高速度對我們?nèi)粘i_發(fā)來說還是很有用的,文中給出了詳細的示例代碼,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-01-01
  • OpenCV實現(xiàn)相機標定板

    OpenCV實現(xiàn)相機標定板

    這篇文章主要為大家詳細介紹了OpenCV實現(xiàn)相機標定板,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-04-04
  • C/C++經(jīng)典楊輝三角問題解決方案

    C/C++經(jīng)典楊輝三角問題解決方案

    楊輝三角形,又稱帕斯卡三角形、賈憲三角形、海亞姆三角形,它的排列形如三角形。本文將為大家介紹通過C++/C語言實現(xiàn)打印楊輝三角形的示例代碼,需要的可以參考一下
    2023-02-02
  • C++模擬實現(xiàn)string的方法詳解

    C++模擬實現(xiàn)string的方法詳解

    標準庫類型string表示可變長的字符序列,使用string類型必須首先包含string的頭文件。本文將利用C++模擬實現(xiàn)string,需要的可以參考一下
    2022-11-11
  • C++趣味算法之偵探推理

    C++趣味算法之偵探推理

    本文詳細講解了C++趣味算法之偵探推理,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • Qt創(chuàng)建并顯示柱狀圖的方法

    Qt創(chuàng)建并顯示柱狀圖的方法

    Qt Charts 模塊提供了一套易于使用的圖表組件,本文主要介紹了Qt創(chuàng)建并顯示柱狀圖,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • C語言動態(tài)內(nèi)存分配和內(nèi)存操作函數(shù)使用詳解

    C語言動態(tài)內(nèi)存分配和內(nèi)存操作函數(shù)使用詳解

    但是在實際的編程中,往往會發(fā)生這種情況,即所需的內(nèi)存空間取決于實際輸入的數(shù)據(jù),而無法預(yù)先確定 。為了解決上述問題,C語言提供了一些內(nèi)存管理函數(shù),這些內(nèi)存管理函數(shù)可以按需要動態(tài)的分配內(nèi)存空間,也可把不再使用的空間回收再次利用
    2022-12-12
  • C語言實現(xiàn)爆炸展開的掃雷詳解

    C語言實現(xiàn)爆炸展開的掃雷詳解

    windows自帶的游戲《掃雷》是陪伴了無數(shù)人的經(jīng)典游戲,本文將利用C語言實現(xiàn)這一經(jīng)典的游戲,文中的示例代碼講解詳細,感興趣的可以學(xué)習(xí)一下,這篇文章主要介紹了C語言實現(xiàn)爆炸展開的掃雷游戲
    2022-07-07
  • c語言實現(xiàn)數(shù)組循環(huán)左移m位

    c語言實現(xiàn)數(shù)組循環(huán)左移m位

    這篇文章主要介紹了c語言實現(xiàn)數(shù)組循環(huán)左移m位,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • C語言安全編碼數(shù)組記法的一致性

    C語言安全編碼數(shù)組記法的一致性

    這篇文章主要介紹了C語言安全編碼數(shù)組記法的一致性,需要的朋友可以參考下
    2014-07-07

最新評論