hash表

来源:互联网 发布:珠海航展歼20知乎 编辑:程序博客网 时间:2024/05/31 00:40
 hash表,有时候也被称为散列表。个人认为,hash表是介于链表和二叉树之间的一种中间结构。链表使用十分方便,但是数据查找十分麻烦;二叉树中的数据严格有序,但是这是以多一个指针作为代价的结果。hash表既满足了数据的查找方便,同时不占用太多的内容空间,使用也十分方便。

    打个比方来说,所有的数据就好像许许多多的书本。如果这些书本是一本一本堆起来的,就好像链表或者线性表一样,整个数据会显得非常的无序和凌乱,在你找到自己需要的书之前,你要经历许多的查询过程;而如果你对所有的书本进行编号,并且把这些书本按次序进行排列的话,那么如果你要寻找的书本编号是n,那么经过二分查找,你很快就会找到自己需要的书本;但是如果你每一个种类的书本都不是很多,那么你就可以对这些书本进行归类,哪些是文学类,哪些是艺术类,哪些是工科的,哪些是理科的,你只要对这些书本进行简单的归类,那么寻找一本书也会变得非常简单,比如说如果你要找的书是计算机方面的书,那么你就会到工科一类当中去寻找,这样查找起来也会显得麻烦。

    不知道这样举例你清楚了没有,上面提到的归类方法其实就是hash表的本质。下面我们可以写一个简单的hash操作代码。

    a)定义hash表和基本数据节点

[cpp] view plaincopy
  1. typedef struct _NODE  
  2. {  
  3.     int data;  
  4.     struct _NODE* next;  
  5. }NODE;  
  6.   
  7. typedef struct _HASH_TABLE  
  8. {  
  9.     NODE* value[10];  
  10. }HASH_TABLE;  

    b)创建hash表
[cpp] view plaincopy
  1. HASH_TABLE* create_hash_table()  
  2. {  
  3.     HASH_TABLE* pHashTbl = (HASH_TABLE*)malloc(sizeof(HASH_TABLE));  
  4.     memset(pHashTbl, 0, sizeof(HASH_TABLE));  
  5.     return pHashTbl;  
  6. }  

    c)在hash表当中寻找数据
[cpp] view plaincopy
  1. NODE* find_data_in_hash(HASH_TABLE* pHashTbl, int data)  
  2. {  
  3.     NODE* pNode;  
  4.     if(NULL ==  pHashTbl)  
  5.         return NULL;  
  6.   
  7.     if(NULL == (pNode = pHashTbl->value[data % 10]))  
  8.         return NULL;  
  9.   
  10.     while(pNode){  
  11.         if(data == pNode->data)  
  12.             return pNode;  
  13.         pNode = pNode->next;  
  14.     }  
  15.     return NULL;  
  16. }  

    d)在hash表当中插入数据
[cpp] view plaincopy
  1. STATUS insert_data_into_hash(HASH_TABLE* pHashTbl, int data)  
  2. {  
  3.     NODE* pNode;  
  4.     if(NULL == pHashTbl)  
  5.         return FALSE;  
  6.   
  7.     if(NULL == pHashTbl->value[data % 10]){  
  8.         pNode = (NODE*)malloc(sizeof(NODE));  
  9.         memset(pNode, 0, sizeof(NODE));  
  10.         pNode->data = data;  
  11.         pHashTbl->value[data % 10] = pNode;  
  12.         return TRUE;  
  13.     }  
  14.   
  15.     if(NULL != find_data_in_hash(pHashTbl, data))  
  16.         return FALSE;  
  17.   
  18.     pNode = pHashTbl->value[data % 10];  
  19.     while(NULL != pNode->next)  
  20.         pNode = pNode->next;  
  21.   
  22.     pNode->next = (NODE*)malloc(sizeof(NODE));  
  23.     memset(pNode->next, 0, sizeof(NODE));  
  24.     pNode->next->data = data;  
  25.     return TRUE;  
  26. }  

    e)从hash表中删除数据
[cpp] view plaincopy
  1. STATUS delete_data_from_hash(HASH_TABLE* pHashTbl, int data)  
  2. {  
  3.     NODE* pHead;  
  4.     NODE* pNode;  
  5.     if(NULL == pHashTbl || NULL == pHashTbl->value[data % 10])  
  6.         return FALSE;  
  7.   
  8.     if(NULL == (pNode = find_data_in_hash(pHashTbl, data)))  
  9.         return FALSE;  
  10.   
  11.     if(pNode == pHashTbl->value[data % 10]){  
  12.         pHashTbl->value[data % 10] = pNode->next;  
  13.         goto final;  
  14.     }  
  15.   
  16.     pHead = pHashTbl->value[data % 10];  
  17.     while(pNode != pHead ->next)  
  18.         pHead = pHead->next;  
  19.     pHead->next = pNode->next;  
  20.   
  21. final:  
  22.     free(pNode);  
  23.     return TRUE;  

二、完整代码

    /*********************************************************************
    *                          哈希表算法实现
    *                       (c)copyright 2013,jdh
    *                         All Right Reserved
    *文件名:main.c
    *程序员:jdh
    **********************************************************************/  
      
    #include <stdio.h>  
    #include <stdlib.h>  
      
    /*********************************************************************
    *                           宏定义
    **********************************************************************/  
      
    /*********************************************************************
    *                           数据类型重定义
    **********************************************************************/  
      
    #define uint8_t unsigned char  
    #define uint16_t unsigned short  
    #define uint32_t unsigned long  
      
    /*********************************************************************
    *                           哈希表长度
    **********************************************************************/  
      
    #define HASH_TABLE_LEN  100  
      
    /*********************************************************************
    *                           数据结构
    **********************************************************************/  
    //链表节点  
    typedef struct _Link_Node    
    {    
        uint16_t id;  
        uint16_t data;  
        struct _Link_Node *next;    
    }Link_Node,*Link_Node_Ptr;   
      
    //哈希表头  
    typedef struct _Hash_Header    
    {    
        struct _Link_Node *next;    
    }Hash_Header,*Hash_Header_Ptr;  
      
    /*********************************************************************
    *                           全局变量
    **********************************************************************/  
      
    //哈希表  
    Hash_Header_Ptr Hash_Table[HASH_TABLE_LEN];  
      
    /*********************************************************************
    *                           函数
    **********************************************************************/  
      
    /*********************************************************************
    *                           哈希表函数
    *说明:
    *1.用哈希函数生成id对应的哈希表中的位置
    输入:id
    返回:位置
    **********************************************************************/  
      
    uint8_t hash_func(uint16_t id)  
    {  
        uint8_t pos = 0;  
          
        pos = id % HASH_TABLE_LEN;  
      
        return pos;  
    }  
      
    /*********************************************************************
    *                           初始化节点
    *返回:结点指针
    **********************************************************************/  
      
    Link_Node_Ptr init_link_node(void)  
    {  
        Link_Node_Ptr node;  
          
        //申请节点  
        node = (Link_Node_Ptr) malloc(sizeof(Link_Node));  
        //初始化长度为0  
        node->next = NULL;  
          
        return node;  
    }  
      
    /*********************************************************************
    *                           初始化哈希表头结点
    *返回哈希表头结点指针
    **********************************************************************/  
      
    Hash_Header_Ptr init_hash_header_node(void)  
    {  
        Hash_Header_Ptr node;  
          
        //申请节点  
        node = (Hash_Header_Ptr) malloc(sizeof(Hash_Header));  
        //初始化长度为0  
        node->next = NULL;  
          
        return node;  
    }  
      
      
    /*********************************************************************
    *                           哈希表初始化
    *说明:
    *1.初始化哈希表Hash_Table
    *2.哈希表长度最大不能超过256
    **********************************************************************/  
      
    void init_hash_table(void)  
    {  
        uint8_t i = 0;  
          
        for (i = 0;i < HASH_TABLE_LEN;i++)  
        {  
            Hash_Table[i] = init_hash_header_node();  
            Hash_Table[i]->next = NULL;  
        }  
    }  
      
    /*********************************************************************
    *                           在哈希表增加节点
    *说明:
    *1.在哈希表的某个链表末增加数据
    输入:new_node:新节点
    **********************************************************************/  
      
    void append_link_node(Link_Node_Ptr new_node)  
    {  
        Link_Node_Ptr node;  
        uint8_t pos = 0;  
          
        //新节点下一个指向为空  
        new_node->next = NULL;  
          
        //用哈希函数获得位置  
        pos = hash_func(new_node->id);  
          
        //判断是否为空链表  
        if (Hash_Table[pos]->next == NULL)  
        {  
            //空链表  
            Hash_Table[pos]->next = new_node;  
        }  
        else  
        {  
            //不是空链表  
            //获取根节点  
            node = Hash_Table[pos]->next;  
          
            //遍历  
            while (node->next != NULL)  
            {  
                node = node->next;  
            }  
              
            //插入  
            node->next = new_node;  
        }  
    }  
      
    /*********************************************************************
    *                           在哈希表查询节点
    *说明:
    *1.知道在哈希表某处的单链表中,并开始遍历.
    *2.返回的是查询节点的前一个节点指针.这么做是为了做删除操作.
    输入:pos:哈希表数组位置,从0开始计数
         id:所需要查询节点的id
         root:如果是根节点,则*root = 1,否则为0
    返回:所需查询的节点的前一个节点指针,如果是根节点则返回根节点,失败返回0
    **********************************************************************/  
      
    Link_Node_Ptr search_link_node(uint16_t id,uint8_t *root)  
    {  
        Link_Node_Ptr node;  
        uint8_t pos = 0;  
          
        //用哈希函数获得位置  
        pos = hash_func(id);  
          
        //获取根节点  
        node = Hash_Table[pos]->next;  
          
        //判断单链表是否存在  
        if (node == NULL)  
        {  
            return 0;  
        }  
          
        //判断是否是根节点  
        if (node->id == id)  
        {  
            //是根节点  
            *root = 1;  
            return node;  
        }  
        else  
        {  
            //不是根节点  
            *root = 0;  
            //遍历  
            while (node->next != NULL)  
            {  
                if (node->next->id == id)  
                {  
                    return node;  
                }  
                else  
                {  
                    node = node->next;  
                }  
            }  
              
            return 0;  
        }  
    }  
      
    /*********************************************************************
    *                           在哈希表删除节点
    *说明:
    *1.删除的不是当前节点,而是当前节点后的一个节点
    输入:node:删除此节点后面的一个节点
         new_node:新节点
    **********************************************************************/  
      
    void delete_link_node(Link_Node_Ptr node)  
    {  
        Link_Node_Ptr delete_node;  
          
        //重定向需要删除的前一个节点  
        delete_node = node->next;  
        node->next = delete_node->next;  
          
        //删除节点  
        free(delete_node);  
        delete_node = NULL;  
    }  
      
    /*********************************************************************
    *                           在哈希表删除根节点
    输入:node:根节点
    **********************************************************************/  
      
    void delete_link_root_node(Link_Node_Ptr node)  
    {  
        uint8_t pos = 0;  
          
        //用哈希函数获得位置  
        pos = hash_func(node->id);  
          
        //哈希表头清空  
        if (node != NULL)  
        {  
            Hash_Table[pos]->next = node->next;  
            //删除节点  
            free(node);  
            node = NULL;  
        }  
    }  
      
    /*********************************************************************
    *                           获得哈希表中所有节点数
    输入:node:根节点
    **********************************************************************/  
      
    uint16_t get_node_num(void)  
    {  
        Link_Node_Ptr node;  
        uint16_t i = 0;  
        uint16_t num = 0;  
          
        //遍历  
        for (i = 0;i < HASH_TABLE_LEN;i++)  
        {  
            //获取根节点  
            node = Hash_Table[i]->next;  
            //遍历  
            while (node != NULL)  
            {  
                num++;  
                node = node->next;  
            }  
        }  
          
        return num;  
    }  
      
    /*********************************************************************
    *                           从哈希表中获得对应序号的节点
    *参数:index:序号.从1开始,最大值为节点总数值
    *     root:如果是根节点,则*root = 1,否则为0
    返回:所需查询的节点的前一个节点指针,如果是根节点则返回根节点,失败返回0
    **********************************************************************/  
      
    Link_Node_Ptr get_node_from_index(uint16_t index,uint8_t *root)  
    {     
        Link_Node_Ptr node;  
        uint16_t i = 0;  
        uint16_t num = 0;  
          
        //遍历  
        for (i = 0;i < HASH_TABLE_LEN;i++)  
        {  
            //获取根节点  
            node = Hash_Table[i]->next;  
            //判断单链表是否存在  
            if (node == NULL)  
            {  
                continue;  
            }  
              
            //根节点  
            num++;  
            if (num == index)  
            {  
                //是根节点  
                *root = 1;  
                return node;   
            }  
              
            //遍历  
            while (node->next != NULL)  
            {  
                num++;  
                if (num == index)  
                {  
                    //不是根节点  
                    *root = 0;  
                    return node;   
                }  
                node = node->next;  
            }  
        }  
          
        return 0;  
    }  
      
    /*********************************************************************
    *                           删除hash表中所有节点
    **********************************************************************/  
      
    void drop_hash()  
    {  
        Link_Node_Ptr node;  
        uint16_t i = 0;  
        Link_Node_Ptr node_next;  
          
        //遍历  
        for (i = 0;i < HASH_TABLE_LEN;i++)  
        {  
            //获取根节点  
            node = Hash_Table[i]->next;  
              
            while (1)  
            {  
                //判断单链表是否存在  
                if (node == NULL)  
                {  
                    //不存在  
                    Hash_Table[i]->next = NULL;  
                    break;  
                }  
                  
                //根节点下一个节点  
                node_next = node->next;  
                //删除根节点  
                free(node);  
                //重指定根节点  
                node = node_next;  
            }  
        }  
    }  
      
    /*********************************************************************
    *                           输出所有节点
    **********************************************************************/  
      
    void printf_hash()  
    {  
        Link_Node_Ptr node;  
        uint8_t root = 0;  
        uint8_t i = 0;  
        uint8_t num = 0;  
          
        printf("-------------打印hash表-------------\n");  
          
        num = get_node_num();  
        for (i = 1;i <= num;i++)  
        {  
            node = get_node_from_index(i,&root);  
            if (node != 0)  
            {  
                if (root)  
                {  
                    printf("根节点:节点号%d,id为%d\n",i,node->id);  
                }  
                else  
                {  
                    printf("普通节点:节点号%d,id为%d\n",i,node->next->id);  
                }  
            }  
        }  
    }  
      
    /*********************************************************************
    *                           主函数
    *说明:实现对哈希表的新建,建立节点,查询及增加,删除节点的操作
    **********************************************************************/  
      
    int main()  
    {  
        Link_Node_Ptr node;  
        uint8_t temp = 0;  
        uint8_t root = 0;  
        uint8_t i = 0;  
          
        init_hash_table();  
          
        //插入数据id = 1,data = 2;  
        node = init_link_node();  
        node->id = 1;  
        node->data = 2;  
        append_link_node(node);  
          
        //查询节点数  
        printf("1节点数为%d\n",get_node_num());  
          
        node = init_link_node();  
        node->id = 100;  
        node->data = 101;  
        append_link_node(node);  
          
        node = init_link_node();  
        node->id = 1002;  
        node->data = 1001;  
        append_link_node(node);  
          
        node = init_link_node();  
        node->id = 10000;  
        node->data = 10001;  
        append_link_node(node);  
          
        node = init_link_node();  
        node->id = 1000;  
        node->data = 10001;  
        append_link_node(node);  
          
        node = init_link_node();  
        node->id = 2;  
        node->data = 10001;  
        append_link_node(node);  
          
        //查询节点数  
        printf("2节点数为%d\n",get_node_num());  
          
        //查询id = 1000;  
        node = search_link_node(100,&temp);  
        if (node != 0)  
        {  
            if (temp == 0)  
            {  
                printf("删除普通节点:所需查询id的值为%d,数据为%d\n",node->next->id,node->next->data);  
                  
                //删除  
                delete_link_node(node);  
            }  
            else  
            {  
                //根节点  
                printf("删除根节点所需查询id的值为%d,数据为%d\n",node->id,node->data);  
                  
                //删除  
                delete_link_root_node(node);  
            }  
        }  
        else  
        {  
            printf("查询失败\n");  
        }  
          
        //查询id = 1000;  
        node = search_link_node(1001,&temp);  
        if (node != 0)  
        {  
            if (temp == 0)  
            {  
                printf("所需查询id的值为%d\n",node->next->data);  
            }  
            else  
            {  
                //根节点  
                printf("所需查询id的值为%d\n",node->data);  
            }  
        }  
        else  
        {  
            printf("查询失败\n");  
        }  
          
        //查询节点数  
        printf("节点数为%d\n",get_node_num());  
          
        printf_hash();  
          
          
        getchar();  
        return 0;  
    } 

0 0