Linux環境編程 哈希鏈表結構 hlist 介紹與用例

hlist原本是定義在內核list.h裏面的結構體,主要用在解決哈希表衝突時使用鏈接(chaining)方法時候用到的結構體。結構體定義簡單、相關的接口也比較豐富,可以直接拿到用戶層使用。最常見的一種使用場景如下圖:


htable是hash數組,每個數組元素是一個hlist_head結構體。當多個結點的hash key值相同時,直接添加在對應的結點鏈表裏就可以了。下面介紹相關數據結構和操作接口使用。

鏈表頭和結點的結構體定義:

/* 鏈表頭 */
struct hlist_head {
	struct hlist_node *first;
};

/* 鏈表結點,具體的數據結構體只需要包含這個結構體就可以了 */
struct hlist_node {
	struct hlist_node *next, **pprev;
}; 

鏈表頭和結點使用前需要初始化:

//hlist_head 初始化
#define HLIST_HEAD_INIT { .first = NULL }    //靜態初始化
#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) //動態初始化

//hlist_node 初始化
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
	h->next = NULL;
	h->pprev = NULL;
}

常用接口包括添加、刪除和修改:

//添加結點至鏈表首部
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h);
//刪除前有判斷
static inline void hlist_del_init(struct hlist_node *n);
//判斷鏈表頭是否爲空,是的話返回1
static inline int hlist_empty(const struct hlist_head *h);
//遍歷結點
hlist_for_each(pos, head) 

//遍歷結點過程中有刪除結點操作使用這個接口
hlist_for_each_safe(pos, n, head) 
//遍歷獲取結點裏的數據
hlist_for_each_entry(tpos, pos, head, member)

//遍歷結點過程中有刪除結點操作使用這個接口
hlist_for_each_entry_safe(tpos, pos, n, head, member) 	

假設結點結構體定義如下;

//結點結構體
struct hdata_node {
    unsigned int data;
    struct hlist_node list;
};

下面是一個小李子,採用除法散列法計算hash值,結點定義如上,包含添加、刪除和查詢操作。

/*
 *  Description : linux應用層編程之哈希鏈表hlist的使用
 *  Date        :20180713
 *  Author      :fuyuande
 *  Mail        : [email protected]
 *
 */


#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

#include "hlistdemo.h"

#define log(fmt, arg...) printf(""fmt,##arg)
#define MAX_ADDR 256

void main(int argc, char **argv){
    struct hlist_head htable[MAX_ADDR];     //hash數組
    struct hdata_node *hnode = NULL;
    struct hlist_node *hlist = NULL, *n = NULL;
    int i = 0, quit = 1, opt = 0, key;
    unsigned int data;

    /* hlist_head 動態初始化 */
    for(i = 0; i < MAX_ADDR; i++)        //hash數組動態初始化
        INIT_HLIST_HEAD(&htable[i]);
        
    log("*********************\n\n"
        "input options:\n"
        "1: insert\n"           //插入
        "2: serach\n"           //查詢
        "3: delete\n"           //刪除
        "0: quit\n"
        "\n*********************\n");  
        
    while(quit != 0){
        log("\ninput options:");
        scanf("%d",  &opt);
        switch(opt){
            //插入
            case  1:
                //分配結點
                hnode = calloc(1, sizeof(struct hdata_node));
                if(!hnode){
                    log("insert fail \n");
                    break;
                }
                //hlist_node 結點初始化
                INIT_HLIST_NODE(&hnode->list);
                
                log("\ninput data:");
                scanf("%d",  &hnode->data);
                key = hnode->data % MAX_ADDR;
                //添加到鏈表首部
                hlist_add_head(&hnode->list, &htable[key]);
                break;

            //查詢
            case  2:
                log("\ninput data:");
                scanf("%d",  &data);         
                key = data % MAX_ADDR;
                if(hlist_empty(&htable[key]))
                    log("data not exist \n");
                else{
                    //遍歷對應的槽位,匹配值就打印
                    hlist_for_each_entry(hnode, hlist, &htable[key], list){
                        if(hnode->data == data)
                            log("find data : %u \n", hnode->data);
                    }
                }                
                break;

            //刪除
            case  3:
                log("\ninput data:");
                scanf("%d",  &data);         
                key = data % MAX_ADDR;
                if(hlist_empty(&htable[key]))
                    log("data not exist ,delete fail \n");
                else{
                    //遍歷對應的槽,匹配值就刪除
                    hlist_for_each_entry_safe(hnode, hlist, n, &htable[key], list){
                        if(hnode->data == data){
                            hlist_del(&hnode->list);
                            break;
                        }
                    }
                }                            
                log("delete fail\n");                
                break;
            case 0:
                quit = 0;
                break;
            default:
                log("unrecognized option!");
                break;                
        }
    }
    //退出程序前釋放資源
    for(i=0; i < MAX_ADDR; i++){
        //遍歷每一個槽,有結點就刪除
        hlist_for_each_entry_safe(hnode, hlist, n, &htable[i], list){           
                hlist_del(&hnode->list); 
                log("delete %u \n", hnode->data);
                free(hnode);
                hnode = NULL;
        }
    }
    log("exit\n");
}

代碼放在:[email protected]:FuYuanDe/hlist_demo.git

git clone之後直接make運行。

またね!



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章