Linux内核常用数据结构——顺序表之哈希表
来源:互联网 发布:进入数据库命令 编辑:程序博客网 时间:2024/06/01 10:51
一、线性表
线性表按照数据结构的存储形式有分为:顺序表和链式表。
顺序表中数据存储的地址在内存中是连续的,所以可以通过计算地址实现随机存取;如:数组、哈希表等。
链式表中数据存储的地址不一定连续,只能通过结点的指针顺序存取;如:我们常用的线性链表、线性循环链表等。
二、顺序表和链式表各自优势
1.顺序表:查找速度快,尤其是哈希表可以根据关键字进行查找、更灵活和方便;缺点是内存必须提前分配好,并且必须是连续内存空间。
2.链式表:内存可以在使用是malloc随机分配;缺点是查找必须单独实现算法,而且算法查找速度慢。
以上就是时间和空间的矛盾。
三、哈希表
1.哈希表与数组的关系
区别:哈希表是通过元素关键码的值直接查找元素存储位置的数据结构;数组是通过下标可以直接访问到下标对应位置上元素的数据结构。
联系:元素的关键码通过散射/哈希函数映射得到的函数值就是哈希表数组的下标(一般的哈希表组织元素的方法还是数组)。
2.哈希冲突算法
因为哈希函数根据关键码计算哈希表数组下标会出现不同关键码计算得到同一个数组下标的可能性;这也是散射/哈希函数不能避免的。
如“除余留数”法实现的哈希函数:hash(key) = key%17;
此时,当key为6、23、40和57时,下标值都为6;这时就需要添加冲突解决。
常用冲突解决有如下两种:
1).再哈希法:采用“再哈希”法解决冲突的哈希表是一个固定大小的结构体数组,然后给哈希表元素设置一个冲突标志位,同时、当执行哈希函数时对使用过的数组下标对应的元素冲突位置1;当下次获得的下标值对应的元素冲突位为1时,则再次利用哈希算法再次算出一个下标值。在查找时,方法类似。下边将实现这种方法。
2).链地址法:采用“链地址”法解决冲突的哈希表是一个固定大小的指针数组,数组的每个元素是一个链表(单向或双向)的头指针。将关键字作为参数、利用哈希函数计算出数据应该属于哈希表中的哪个指针数组;然后,从该指针数组所指地址处构建线性链表。Linux2.6内核的哈希表就是采用这种方法实现。其实这种方法是将哈希查找算法和链表有机结合起来。不仅利用了hash提高查找速度,并且能很好的解决冲突;同时、比起其他哈希表,该方法中元素是指针(哈希表是一个指针数组)、这时除了指针数组元素空间需要提前分配外,具体数据存储还是动态分配的、提高了内存使用率。这种方法在内存使用率和查找效率上是一个很好的权衡。
最后,总的来说、哈希表的查询是飞快的。因为它不需要从头搜索,它利用Key的“哈希算法”直接定位,查找非常快,各种数据库中的数据结构基本都是它。但带来的问题是,哈希表的尺寸、哈希算法。
3.看看我们的demo
test.c
#include <stdio.h>/*关键在于creathashaddr和hashsearch函数的实现;关键点是哈希表的构造方法和哈希冲突的解决算法本demo哈希表的构造采用“除留余数”法,处理冲突采用“再哈希”法。而Linux2.6内核处理冲突使用的是“链地址”法、因此会看到结构体中有线性链表存在。下面从设计思想上说下链地址法: 其实这种方法是将哈希查找算法和链表有机结合起来。不仅利用了hash提高查找速度,并且能很好的解决冲突;同时、比起其他方法,由于哈希表中元素是指针(哈希表是一个指针数组)、这时除了指针数组元素空间需要提前分配外,具体数据存储还是动态分配的、提高了内存使用率。这种方法在内存使用率和查找效率上是一个很好的权衡。*/#define HASH_SIZE 17typedef struct node{ char *name; int age; int flag;//标志位,当前节点是否冲突;Linux2.6内核中“链地址”法,此处是一个链表指针}mynode;mynode hashlist[HASH_SIZE];//创建哈希表int creathashaddr(int key){ int i; int addr = -1; for(i=0; i < HASH_SIZE; i++){ addr = key%HASH_SIZE; if(hashlist[addr].flag == 0){ hashlist[addr].flag = 1; return addr; } else{//哈希冲突 printf("TK------->>>>gethashaddr is chongtu!!!!!\n");//add by tankai do{ addr = (key + addr%10 + 1)%HASH_SIZE; }while(hashlist[addr].flag != 0);//二次哈希冲突 hashlist[addr].flag = 1; return addr; } }}void hashsearch(int age){ int addr = age%HASH_SIZE; if(hashlist[addr].age == age){ printf("TK--------->>>>>>hashlist[%d].name is %s\n",addr,hashlist[addr].name); return; } else if(hashlist[addr].flag == 0){ printf("TK------>>1111>>no this!\n"); return; } else{//哈希冲突 do{ addr = (age + addr%10 + 1)%HASH_SIZE; if(hashlist[addr].age == age){ printf("TK--------->>>>>>hashlist[%d].name is %s\n",addr,hashlist[addr].name); return; } }while(hashlist[addr].flag != 0);//二次哈希冲突 } printf("TK------>>2222>>no this!\n"); return;}int main(){ int i; for (i=0; i<HASH_SIZE; i++) { hashlist[i].name=""; hashlist[i].age=0; hashlist[i].flag=0; } int j = creathashaddr(23); hashlist[j].name = "tan"; hashlist[j].age = 23; printf("TK--------->>>>>>age is %d,hashlist[%d].name is %s\n",hashlist[j].age,j,hashlist[j].name); /// j = creathashaddr(40); hashlist[j].name = "kai"; hashlist[j].age = 40; printf("TK--------->>>>>>age is %d,hashlist[%d].name is %s\n",hashlist[j].age,j,hashlist[j].name); /// j = creathashaddr(6); hashlist[j].name = "tankai"; hashlist[j].age = 6; printf("TK--------->>>>>>age is %d,hashlist[%d].name is %s\n",hashlist[j].age,j,hashlist[j].name); //// int test; do{ printf("#######please input user age:##########\n"); scanf("%d",&test); printf("TK--------->>>>>age is %d\n",test); hashsearch(test); }while(test != 0); return 0;}/*gcc test.c -o test./testresult is : TK--------->>>>>>age is 23,hashlist[6].name is tanTK------->>>>gethashaddr is chongtu!!!!!TK--------->>>>>>age is 40,hashlist[13].name is kaiTK------->>>>gethashaddr is chongtu!!!!!TK--------->>>>>>age is 6,hashlist[10].name is tankai#######please input user age:##########23TK--------->>>>>age is 23TK--------->>>>>>hashlist[6].name is tan#######please input user age:##########40TK--------->>>>>age is 40TK--------->>>>>>hashlist[13].name is kai#######please input user age:##########6TK--------->>>>>age is 6TK--------->>>>>>hashlist[10].name is tankai#######please input user age:##########57TK--------->>>>>age is 57TK------>>2222>>no this!#######please input user age:##########5TK--------->>>>>age is 5TK------>>1111>>no this!#######please input user age:##########*/
gcc test.c -o test
./test
TK--------->>>>>>age is 23,hashlist[6].name is tanTK------->>>>gethashaddr is chongtu!!!!!TK--------->>>>>>age is 40,hashlist[13].name is kaiTK------->>>>gethashaddr is chongtu!!!!!TK--------->>>>>>age is 6,hashlist[10].name is tankai#######please input user age:##########23TK--------->>>>>age is 23TK--------->>>>>>hashlist[6].name is tan#######please input user age:##########40TK--------->>>>>age is 40TK--------->>>>>>hashlist[13].name is kai#######please input user age:##########6TK--------->>>>>age is 6TK--------->>>>>>hashlist[10].name is tankai#######please input user age:##########57TK--------->>>>>age is 57TK------>>2222>>no this!#######please input user age:##########5TK--------->>>>>age is 5TK------>>1111>>no this!#######please input user age:##########
- Linux内核常用数据结构——顺序表之哈希表
- Linux内核常用数据结构
- 数据结构之顺序表常用操作整理
- linux 内核常用数据结构及算法——container_of
- 数据结构之——C++顺序表
- linux 内核常用数据结构及算法——list(循环双向链表)
- linux内核数据结构-顺序存储链表(1)
- Linux内核常用数据结构要点
- Linux内核常用数据结构要点
- Linux内核数据结构—链表
- linux内核数据结构—kfifo
- Linux内核数据结构之链表
- linux内核数据结构之链表
- linux内核数据结构之链表
- linux内核数据结构之链表
- linux内核数据结构之链表
- linux内核 路由fib表之数据结构
- linux内核 路由缓存表之数据结构
- Linux内核系统定时器TIMER实现过程分析
- eclips 隐藏QuickAccess
- 遍历Map集合方式
- javascript 怎么获取文件上传对话框中的全路径名
- 最考验勇气办公室 处在2133米高空
- Linux内核常用数据结构——顺序表之哈希表
- 分享一款免费在线做图工具:ProcessOn
- 枚举排列问题:生成1-n的排列和可重集排列问题
- oracle表分区详解
- C/C++标准输入输出与文件输入输出
- 最长回文串算法
- linux文件操作open close read write lseek
- mysql的三种安装方式:RPM 二进制包和源代码
- 程序员如何保持优秀