转自:http://blog.chinaunix.net/uid-28458801-id-4276934.html

操作系统:ubuntu10.04

前言:
    在稍微大点的项目中,基本都会遇到算法问题,特别是大数据的查找。
    在当前项目中,使用到了哈希链表。

一,概述
    实现思路:用数组保存哈希桶的关键信息,再用链表链接数据到对应的哈希桶中。
        如:管理很多字符串。以a~z,?为哈希桶。
    

二,实现
    1,结构

点击(此处)折叠或打开

  1. struct    hlist_node
  2. {
  3. struct hlist_node     *next;    // 指向下一个结点的指针
  4. struct hlist_node    **pprev;// 指向上一个结点的next指针的地址
  5. };
  6. struct     hlist_head
  7. {
  8. struct hlist_node *first;    // 指向每一个hash桶的第一个结点的指针
  9. };

2,初始化哈希桶

点击(此处)折叠或打开

  1. // 初始化hash桶的头结点
  2. #define    INIT_HLIST_HEAD(ptr)    ((ptr)->first = NULL)

3,初始化哈希桶中的每一个节点

点击(此处)折叠或打开

  1. // 初始化hash桶的普通结点
  2. static    inline    void    INIT_HLIST_NODE(struct hlist_node *node)
  3. {
  4. node->next    = NULL;
  5. node->pprev    = NULL;
  6. }

4,添加节点到哈希桶中

点击(此处)折叠或打开

  1. /**
  2. * hlist_add_head
  3. * @n: the element to add to the hash list.
  4. * @h: the list to add to.
  5. */
  6. static    inline    void    hlist_add_head(struct hlist_node *n,struct hlist_head *h)
  7. {
  8. struct hlist_node    *first = h->first;
  9. n->next        = first;
  10. if (first)
  11. first->pprev    = &n->next;
  12. h->first     = n;
  13. n->pprev    = &h->first;
  14. }


    

5,把节点插入到指定节点前面

点击(此处)折叠或打开

  1. /* next must be != NULL */
  2. /* n:要添加的新的节点。
  3. * next:在next节点之前添加n。
  4. * 在next节点的前面添加一个新的节点n,在使用这个函数中要特别注意,next不能为NULL。
  5. */
  6. static    inline    void    hlist_add_before(struct hlist_node *n,
  7. struct hlist_node *next)
  8. {
  9. n->pprev    = next->pprev;
  10. n->next        = next;
  11. next->pprev    = &n->next;
  12. *(n->pprev)    = n;
  13. }

6,把节点插入到指定节点之后

点击(此处)折叠或打开

  1. /* next must be != NULL */
  2. /* n:要添加的新的节点。
  3. * next:表示在next节点之后添加n。
  4. * 在next 节点的后面添加一个新的节点n,这里也要求next不能为NULL
  5. */
  6. static inline void hlist_add_after(struct hlist_node *n,
  7. struct hlist_node *next)
  8. {
  9. n->next = next->next;
  10. next->next = n;
  11. n->pprev = &next->next;
  12. if(n->next)
  13. n->next->pprev = &n->next;
  14. }

7,删除节点 

点击(此处)折叠或打开

  1. /* n:要删除的节点。
  2. * 对于删除操作的话,要注意n是不是末尾节点,如果是末尾节点的话,next就是NULL?
  3. * 所以就没有指向的pprev,就更不能进行相应的修改了,否则进行修改。
  4. */
  5. static inline void __hlist_del(struct hlist_node *n)
  6. {
  7. struct hlist_node *next = n->next;
  8. struct hlist_node **pprev = n->pprev;
  9. *pprev = next;
  10. if (next)
  11. next->pprev = pprev;
  12. }
  13. /* n:要删除的节点。
  14. * 在这个函数中首先删除了n节点,之后将n节点的两个指针指向了LIST_POSION,表示不可使用的地方
  15. */
  16. static inline void hlist_del(struct hlist_node *n)
  17. {
  18. __hlist_del(n);
  19. n->next     = LIST_POISON1;
  20. n->pprev    = LIST_POISON2;
  21. }

8,判断节点是否在哈希桶中

点击(此处)折叠或打开

  1. /*
  2. * 判断一个结点是否已经存在于hash桶中
  3. * 判断h->prev是不是为空,如果pprev的指向是空的话,表示这个节点没有添加到这个链表当中来,
  4. * 如果是空,返回true,否则返回false
  5. */
  6. static inline int hlist_unhashed(const struct hlist_node *h)
  7. {
  8. return !h->pprev;
  9. }

    9,判断哈希桶是否为空

点击(此处)折叠或打开

  1. // 判断一个hash桶是否为空
  2. /* h:struct hlist_head节点指针(hlist链表的头节点)。
  3. * 判断hlist链表是不是空链表,如果是,返回true,否则返回false。
  4. */
  5. static inline int hlist_empty(const struct hlist_head *h)
  6. {
  7. return !h->first;
  8. }

10,遍历

点击(此处)折叠或打开

  1. /* ptr:表示struct hlist_node类型的一个地址。
  2. * type:结构体名
  3. * member:type结构体中的hlist_node成员变量的名称
  4. * 表示得到ptr所指地址的这个结构体的首地址
  5. */
  6. #define hlist_entry(ptr, type, member) container_of(ptr,type,member)
  7. /* pos:struct hlist_node类型的一个指针;
  8. * head:struct hlist_head类型的一个指针,表示hlist链表的头结点。
  9. * 这个实际上就是一个for循环,从头到尾遍历链表。
  10. */
  11. #define hlist_for_each(pos, head) \
  12. for (pos = (head)->first; pos != NULL ; 1; }); \
  13. pos = pos->next)
  14. /* 这个实际上就是一个for循环,从头到尾遍历链表。这个和前面的不同的是多了一个n,
  15. * 这么做是为了遍历过程中防止断链的发生。删除时用这个。
  16. * pos:struct hlist_node类型的一个指针;
  17. * n:struct hlist_node类型的一个指针;
  18. * head:struct hlist_head类型的一个指针,表示hlist链表的头结点。
  19. */
  20. #define hlist_for_each_safe(pos, n, head) \
  21. for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
  22. pos = n)
  23. /* tops:用来存放遍历到的数据结构的地址,类型是type *;
  24. * pos:struct hlist_node类型的一个指针;
  25. * head:hlist链表的头结点;
  26. * member:struct hlist_node在type结构体中的变量的名称。
  27. * 在循环中,我们就可以使用tops来指向type类型结构体的任何一个变量了。
  28. */
  29. /**
  30. * hlist_for_each_entry    - iterate over list of given type
  31. * @tpos:    the type * to use as a loop cursor.
  32. * @pos:    the &struct hlist_node to use as a loop cursor.
  33. * @head:    the head for your list.
  34. * @member:    the name of the hlist_node within the struct.
  35. */
  36. #define hlist_for_each_entry(tpos, pos, head, member)             \
  37. for (pos = (head)->first;                     \
  38. (pos != NULL) &&             \
  39. ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
  40. pos = pos->next)
  41. /* tops:用来存放遍历到的数据结构的地址,类型是type *;
  42. * pos:struct hlist_node类型的一个指针;
  43. * n:struct hlist_node类型的一个指针;
  44. * head:hlist链表的头结点;
  45. * member:struct hlist_node在type结构体中的变量的名称。
  46. * 在循环中,我们就可以使用tops来指向type
  47. * 类型结构体的任何一个变量了。这个宏函数也是为了防止在遍历的时候删除节点而引入的。
  48. */
  49. /**
  50. * hlist_for_each_entry_safe - iterate over list of given type safe against
  51. removal of list entry
  52. * @tpos:    the type * to use as a loop cursor.
  53. * @pos:    the &struct hlist_node to use as a loop cursor.
  54. * @n:        another &struct hlist_node to use as temporary storage
  55. * @head:    the head for your list.
  56. * @member:    the name of the hlist_node within the struct.
  57. */
  58. #define hlist_for_each_entry_safe(tpos, pos, n, head, memsber)          \
  59. for (pos = (head)->first;                     \
  60. pos && ({ n = pos->next; 1; }) &&                  \
  61. ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
  62. pos = n)

三,实例

点击(此处)折叠或打开

  1. /*
  2. * hlist begin
  3. */
  4. #define        CMD_HASH_HEAD_SIZE        27
  5. typedef    struct _cmd_hash_head
  6. {
  7. struct     hlist_head        head;        // 哈希桶的首地址
  8. int8_t                    ch;            // 哈希桶的关键字(a~z,?,总共27个)
  9. int8_t                    offset;        // 这个哈希桶在整个哈希表中的偏移
  10. int16_t                    count;        // 当前哈希桶中节点的个数
  11. }cmd_hash_head_t;
  12. typedef    struct    _cmd_hash_node
  13. {
  14. struct    hlist_node        node;
  15. int8_t                    name[20];
  16. }cmd_hash_node_t;
  17. static    cmd_hash_head_t        cmd_hash[CMD_HASH_HEAD_SIZE];
  18. static    void    cmd_hash_init(void)
  19. {
  20. int32_t        index = 0;
  21. memset(cmd_hash,0,sizeof(cmd_hash));
  22. for(index = 0; index < (CMD_HASH_HEAD_SIZE - 1); index++)
  23. {
  24. INIT_HLIST_HEAD(&cmd_hash[index].head);
  25. cmd_hash[index].count    = 0;
  26. cmd_hash[index].offset    = index;
  27. cmd_hash[index].ch        = 'a' + index;
  28. }
  29. index = CMD_HASH_HEAD_SIZE - 1;
  30. INIT_HLIST_HEAD(&cmd_hash[index].head);
  31. cmd_hash[index].count    = 0;
  32. cmd_hash[index].offset    = index;
  33. cmd_hash[index].ch        = '?';
  34. }
  35. static    void    cmd_hash_show(void)
  36. {
  37. int32_t        index = 0;
  38. for (index = 0; index < (CMD_HASH_HEAD_SIZE - 1); index++)
  39. printf("hash%d,head : %p,count : %d,offset : %d,ch : %c\n",index,
  40. cmd_hash[index].head,cmd_hash[index].count,cmd_hash[index].offset,cmd_hash[index].ch);
  41. index = CMD_HASH_HEAD_SIZE - 1;
  42. printf("hash%d,head : %p,count : %d,offset : %d,ch : %c\n",index,
  43. cmd_hash[index].head,cmd_hash[index].count,cmd_hash[index].offset,cmd_hash[index].ch);
  44. }
  45. static    void    to_lower(int8_t    *str)
  46. {
  47. int32_t        len     = 0;
  48. int32_t        index     = 0;
  49. if (str == NULL)    return;
  50. len     = strlen((char*)str);
  51. for (index = 0; index < len; index++)
  52. {
  53. str[index] = tolower(str[index]);
  54. }
  55. }
  56. static    int8_t    node_type(int8_t    *name)
  57. {
  58. int8_t    ch         = 0x00;
  59. int8_t    offset     = 0;
  60. if (name == NULL)    return -1;
  61. ch    = name[0];
  62. switch(ch)
  63. {
  64. case 'a':
  65. case 'b':
  66. case 'c':
  67. case 'd':
  68. case 'e':
  69. case 'f':
  70. case 'g':
  71. case 'h':
  72. case 'i':
  73. case 'j':
  74. case 'k':
  75. case 'l':
  76. case 'm':
  77. case 'n':
  78. case 'o':
  79. case 'p':
  80. case 'q':
  81. case 'r':
  82. case 's':
  83. case 't':
  84. case 'u':
  85. case 'v':
  86. case 'w':
  87. case 'x':
  88. case 'y':
  89. case 'z':
  90. offset = ch - 'a' + 0;
  91. break;
  92. default :
  93. offset = 26;
  94. break;
  95. }
  96. return offset;
  97. }
  98. //static    cmd_hash_node_t ptr_buffer[100];
  99. static    void    hash_node_init(int8_t    *name)
  100. {
  101. int8_t        offset = 0;
  102. cmd_hash_node_t *node_ptr = (cmd_hash_node_t*)calloc(1,sizeof(cmd_hash_node_t));
  103. offset = node_type(name);
  104. if(offset < 0)    return;
  105. strlcpy(node_ptr->name, name, strlen((char*)name));
  106. INIT_HLIST_NODE(&node_ptr->node);
  107. hlist_add_head(&node_ptr->node,&cmd_hash[offset].head);
  108. cmd_hash[offset].count++;
  109. }
  110. static    void    cmd_hash_node_init(void)
  111. {
  112. hash_node_init((int8_t*)"hello");
  113. hash_node_init((int8_t*)"help");
  114. hash_node_init((int8_t*)"scan");
  115. hash_node_init((int8_t*)"???");
  116. }
  117. static    void    cmd_hash_node_show(void)
  118. {
  119. int32_t        index = 0;
  120. int16_t        count = 0;
  121. cmd_hash_node_t    *entry = NULL;
  122. struct hlist_node *ptr = NULL;
  123. printf("display\n");
  124. for (index = 0; index < CMD_HASH_HEAD_SIZE; index++)
  125. {
  126. count = cmd_hash[index].count;
  127. if (count > 0)
  128. {
  129. printf("hash%d,head : %p,count : %d,offset : %d,ch : %c\n",index,
  130. cmd_hash[index].head,cmd_hash[index].count,cmd_hash[index].offset,cmd_hash[index].ch);
  131. hlist_for_each_entry(entry,ptr, &cmd_hash[index].head, node)
  132. {
  133. printf("    name : %s\n",entry->name);
  134. }
  135. }
  136. }
  137. }
  138. static    void    test_hlist_oper(void)
  139. {
  140. cmd_hash_init();
  141. cmd_hash_show();
  142. cmd_hash_node_init();
  143. cmd_hash_show();
  144. cmd_hash_node_show();
  145. }
  146. static    void    test_hlist(void)
  147. {
  148. test_hlist_oper();
  149. }
  150. /*
  151. * hlist end
  152. */

结果:
    

四,参考
1,linux内核源码

linux下C语言实现的哈希链表【转】的更多相关文章

  1. linux 下C语言学习路线

    UNIX/Linux下C语言的学习路线.一.工具篇“公欲善其事,必先利其器”.编程是一门实践性很强的工作,在你以后的学习或工作中,你将常常会与以下工具打交道, 下面列出学习C语言编程常常用到的软件和工 ...

  2. Linux下C语言编程实现spwd函数

    Linux下C语言编程实现spwd函数 介绍 spwd函数 功能:显示当前目录路径 实现:通过编译执行该代码,可在终端中输出当前路径 代码实现 代码链接 代码托管链接:spwd.c 所需结构体.函数. ...

  3. Linux基础与Linux下C语言编程基础

    Linux基础 1 Linux命令 如果使用GUI,Linux和Windows没有什么区别.Linux学习应用的一个特点是通过命令行进行使用. 登录Linux后,我们就可以在#或$符后面去输入命令,有 ...

  4. LINUX下C语言编程基础

    实验二 Linux下C语言编程基础 一.实验目的 1. 熟悉Linux系统下的开发环境 2. 熟悉vi的基本操作 3. 熟悉gcc编译器的基本原理 4. 熟练使用gcc编译器的常用选项 5 .熟练使用 ...

  5. LINUX下中文语言包的安装(转)

    在安装盘上已经有各种语言包了,我们只需要找到他们,并安装就可以了.中文的是fonts-chinese-3.02-9.6.el5.noarch.rpmfonts-ISO8859-2-75dpi-1.0- ...

  6. Unix和Linux下C语言学习指南

    转自:http://www.linuxdiyf.com/viewarticle.php?id=174074 Unix和Linux下C语言学习指南 引言 尽管 C 语言问世已近 30 年,但它的魅力仍未 ...

  7. 笔记整理——Linux下C语言正则表达式

    Linux下C语言正则表达式使用详解 - Google Chrome (2013/5/2 16:40:37) Linux下C语言正则表达式使用详解 2012年6月6日Neal627 views发表评论 ...

  8. linux下常用语言的语法检查插件整理

    linux下常用语言的语法检查插件 可以结合vim语法检查插件syntastic使用,具体请参考syntastic使用说明 如php,sql,json,css,js,html,shell,c等语法插件 ...

  9. LINUX下C语言编程调用函数、链接头文件以及库文件

    LINUX下C语言编程经常需要链接其他函数,而其他函数一般都放在另外.c文件中,或者打包放在一个库文件里面,我需要在main函数中调用这些函数,主要有如下几种方法: 1.当需要调用函数的个数比较少时, ...

随机推荐

  1. HashMap的扩容机制以及默认大小为何是2次幂

    HashMap的Put方法 回顾HashMap的put(Key k, Value v)过程: (1)对 Key求Hash值,对n-1取模计算出Hash表数组下标 (2)如果没有碰撞,直接放入桶中,即H ...

  2. Linux下启用MySQL慢查询

    MySQL在linux系统中的配置文件一般是my.cnf找到[mysqld]下面加上log-slow-queries=/data/mysqldata/slowquery.loglong_query_t ...

  3. UVA11735_Corner the Queens

    题目是这样的,游戏规则,每个人轮流将二维空间上的皇后往下,往左或者往斜下45度的方向移动. 谁第一个移动到0,0的位置就获胜. 题目给定你若干个矩形,求矩形中任取一点且该点必胜的概率有概率. 其实是这 ...

  4. KMP算法模板(pascal)

    洛谷P3375: program rrr(input,output); var i,j,lena,lenb:longint; a,b:ansistring; next:..]of longint; b ...

  5. 【uoj#207】共价大爷游长沙 随机化+LCT维护子树信息

    题目描述 给出一棵树和一个点对集合S,多次改变这棵树的形态.在集合中加入或删除点对,或询问集合内的每组点对之间的路径是否都经过某条给定边. 输入 输入的第一行包含一个整数 id,表示测试数据编号,如第 ...

  6. LR安装No Background bmp defined in section General entry BGBmp的解决办法

    问题描述:我在win10装LR11总是报这个错误:No Background bmp defined in section "General" entry "BGBmp& ...

  7. Day 2 while循环 编码 and or not

    1.判断下列逻辑语句的True,False. 1)1 > 1 or 3 < 4 or 4 > 5 and 2 > 1 and 9 > 8 or 7 < 6 Flas ...

  8. C++解析(17):操作符重载

    0.目录 1.操作符重载 2.完善的复数类 3.小结 1.操作符重载 下面的复数解决方案是否可行? 示例1--原有的解决方案: #include <stdio.h> class Compl ...

  9. KMP算法复习【+继续学习】

    离NOIP还剩12天,本蒟蒻开始准备复习了. 先来个KMP[似乎我并没有写过KMP的blog] KMP KMP算法是解决字符串匹配问题的一个算法,主要是单对单的字符串匹配加速,时间复杂度O(m + n ...

  10. 【loj2472】IIIDX

    Portal --> loj2472 Solution 感觉是一道很有意思的贪心题啊ovo(想了一万个假做法系列==) 比较直观的想法是,既然一个数\(i\)只会对应一个\(\lfloor\fr ...