哈希表

散列表（）是根据键而直接访问在记忆体储存位置的数据结构。也就是说，它通过计算出一个键值的函数，将所需查询的数据映射到表中一个位置来让人访问，这加快了查找速度。这个映射函数称做散列函数，存放记录的数组称做散列表。
一个通俗的例子是，为了查找电话簿中某人的号码，可以创建一个按照人名首字母顺序排列的表（即建立人名formula_1到首字母formula_2的一个函数关系），在首字母为W的表中查找“王”姓的电话号码，显然比直接查找就要快得多。这里使用人名作为关键字，“取首字母”是这个例子中散列函数的函数法则formula_3，存放首字母的表对应散列表。关键字和函数法则理论上可以任意确定。
构造散列函数.
散列函数能使对一个数据序列的访问过程更加迅速有效，通过散列函数，数据元素将被更快定位。
处理冲突.
为了知道冲突产生的相同散列函数地址所对应的关键字，必须选用另外的散列函数，或者对冲突结果进行处理。而不发生冲突的可能性是非常之小的，所以通常对冲突进行处理。常用方法有以下几种：
显示线性探测填装一个散列表的过程：
聚集（Cluster，也翻译做“堆积”）的意思是，在函数地址的表中，散列函数的结果不均匀地占据表的单元，形成区块，造成线性探测产生一次聚集（primary clustering）和平方探测的二次聚集（secondary clustering），散列到区块中的任何关键字需要查找多次试选单元才能插入表中，解决冲突，造成时间浪费。对于开放定址法，聚集会造成性能的灾难性损失，是必须避免的。
例程.
在C语言中，实现以上过程的简要程序：
// HashTable
InitializeTable(int TableSize) {
 HashTable H;
 int i;
 // 为散列表分配空间
 // 有些编译器不支持为struct HashTable 分配空间，声称这是一个不完全的结构，
 // 可使用一个指向HashTable的指针为之分配空间。
 // 如：sizeof(Probe)，Probe作为HashTable在typedef定义的指针。
 H = malloc(sizeof(struct HashTable));
 // 散列表大小为一个质数
 H-&gt;TableSize = Prime;
 // 分配表所有地址的空间
 H-&gt;Cells = malloc(sizeof(Cell) * H-&gt;TableSize);
 // 地址初始为空
 for (i = 0; i &lt; H-&gt;TableSize; i++)
 H-&gt;Cells[i].info = Empty;
 return H;
查找空单元并插入：
// Position
Find(ElementType Key, HashTable H) {
 Position Current;
 int CollisionNum;
 // 冲突次数初始为0
 // 通过表的大小对关键字进行处理
 CollisionNum = 0;
 Current = Hash( Key, H-&gt;TableSize );
 // 不为空时进行查询
 while (H-&gt;Cells[Current].info != Empty &amp;&amp;
 H-&gt;Cells[Current].Element != Key) {
 Current = ++CollosionNum * ++CollisionNum;
 // 向下查找超过表范围时回到表的开头
 if (Current &gt;= H-&gt;TableSize)
 Current -= H-&gt;TableSize;
 return Current;
查找效率.
散列表的查找过程基本上和造表过程相同。一些关键码可通过散列函数转换的地址直接找到，另一些关键码在散列函数得到的地址上产生了冲突，需要按处理冲突的方法进行查找。在介绍的三种处理冲突的方法中，产生冲突后的查找仍然是给定值与关键码进行比较的过程。所以，对散列表查找效率的量度，依然用平均查找长度来衡量。
查找过程中，关键码的比较次数，取决于产生冲突的多少，产生的冲突少，查找效率就高，产生的冲突多，查找效率就低。因此，影响产生冲突多少的因素，也就是影响查找效率的因素。影响产生冲突多少有以下三个因素：
载荷因子.
散列表的载荷因子定义为：formula_33 = 填入表中的元素个数 / 散列表的长度
formula_33是散列表装满程度的标志因子。由于表长是定值，formula_33与“填入表中的元素个数”成正比，所以，formula_33越大，表明填入表中的元素越多，产生冲突的可能性就越大；反之，formula_33越小，标明填入表中的元素越少，产生冲突的可能性就越小。实际上，散列表的平均查找长度是载荷因子formula_33的函数，只是不同处理冲突的方法有不同的函数。
对于开放定址法，荷载因子是特别重要因素，应严格限制在0.7-0.8以下。超过0.8，查表时的CPU缓存不命中（cache missing）按照指数曲线上升。因此，一些采用开放定址法的hash库，如Java的系统库限制了荷载因子为0.75，超过此值将resize散列表。
举例：Linux内核的bcache.
Linux操作系统在物理文件系统与块设备驱动程序之间引入了“缓冲区缓存”（Buffer
Cache，简称bcache）。当读写磁盘文件的数据，实际上都是对bcache操作，这大大提高了读写数据的速度。如果要读写的磁盘数据不在bcache中，即缓存不命中（miss），则把相应数据从磁盘加载到bcache中。一个缓存数据大小是与文件系统上一个逻辑块的大小相对应的（例如1KiB字节），在bcache中每个缓存数据块用codice_1记载其元信息：
struct buffer_head {
 char *b_data; // 指向缓存的数据块的指针
 unsigned long b_blocknr; // 逻辑块号
 unsigned short b_dev; // 设备号
 unsigned char b_uptodate; // 缓存中的数据是否是最新的
 unsigned char b_dirt; // 缓存中数据是否为脏数据
 unsigned char b_count; // 这个缓存块被引用的次数
 unsigned char b_lock; // b_lock表示这个缓存块是否被加锁
 struct task_struct *b_wait; // 等待在这个缓存块上的进程
 struct buffer_head *b_prev; // 指向缓存中相同hash值的下一个缓存块
 struct buffer_head *b_next; // 指向缓存中相同hash值的上一个缓存块
 struct buffer_head *b_prev_free; // 缓存块空闲链表中指向下一个缓存块
 struct buffer_head *b_next_free; // 缓存块空闲链表中指向上一个缓存块
整个bcache以codice_1为基本数据单元，组织为一个封闭定址（close addressing，即“单独链表法”解决冲突）的散列表codice_3 散列函数的输入关键字是b_blocknr（逻辑块号）与b_dev（设备号）。计算hash值的散列函数表达式为：
其中NR_HASH是散列表的条目总数。发生“ 冲突”的codice_1，以b_prev与b_next指针组成一个双向（不循环）链表。bcache中所有的codice_1，包括使用中不空闲与未使用空闲的codice_1，以b_prev_free和b_next_free指针组成一个双向循环链表free_list，其中未使用空闲的codice_1放在该链表的前部。