body, table{font-family: 微软雅黑; font-size: 13.5pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}

  散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key),adr = f(key)。查找时,根据这个确定的对应关系找到给定值key的映射f(key),若查找集合中存在这个记录,则必定在f(key)的位置上。
  这里我们把这种对应关系f称为散列函数,又称为哈希函数(Hash).按这个思想,采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间称为散列表或哈希表(Hash table)。关键字对应的记录存储位置称为散列地址。

散列技术既是一种存储方法,也是一种查找方法。
散列技术最适合求解的问题是查找与给定值相等的记录。

散列函数设计原则:简单、均匀、存储利用率高

两个关键字 key1 ≠ key2,但是却有 f(key1) ≠ f(key2),这种现象我们称为冲突(collision),并把key1和key2称为这个散列函数的同义词(synonym)。

散列函数设计原则:
  计算简单: 散列函数的计算时间不应该超过其他查找技术与关键字比较的时间。
  散列地址分布均匀:尽量让散列地址均匀地分布在存储空间中,这样可以保证存储空间的有效利用,并减少为处理冲突而耗费的时间。
直接定址法 f(key) = a * key + b(a、b为常数)
  取关键字的某个线性函数值为散列地址。(这样的散列函数简单、均匀、不会产生冲突;需要事先知道关键字的分布情况,适合查找表较小且连续的情况,较少使用)
数字分析法
  抽取关键字的一部分来计算散列函数位置的方法。适合处理关键字位数比较大的情况,(如果事先知道关键字的分布且关键字的若干位分布比较均匀,就可以考虑用这个方法。)
平方取中法
  计算关键字的平方,再取中间几位做为关键字。eg:1234,平方1522756,取中间3为227为关键字。(平方取中法比较适合不知道关键字的分布,而位数又不是很大的情况。)
折叠法
  将关键字从左到右分割成位数相等的几部分,然后将这几部分叠加求和,并按散列表表长,取后几位作为散列表地址。(折叠法事先不需要知道关键字的分布,适合关键字位数较多的情况。)
除留余数法
  此方法为最常用的构造散列函数的方法。对于散列表长为m的散列函数公式为:f(key) = key mod p(p≤m)
这种方法不仅可以对关键字直接取模,也可在折叠、平方取中后再取模。**散列表长为m,通常p为小于或等于表长(最好接近m)的最小质数或不包含小于20质因子的合数。
随机数法
  选择一个随机数,取关键字的随机函数值为它的散列地址,也就是 f(key) = random(key)。这里random是随机函数。(当关键字的长度不等时,采用这个方法构造散列函数是比较合适的)

选择散列函数应该考虑事项:
1、计算散列地址所需时间
2、关键字的长度
3、散列表的大小
4、关键字的分布情况
5、记录查找的频率

处理散列冲突的方法
开放定址法
  一旦发生了冲突,就去寻找下一个空的散列地址,知道散列表足够大,空的散列表地址总能找到,并将记录存入。这种解决冲突的开放定址发称为线性探测法。
  fi(key) = (f(key) + di) MOD m (di = 1,2,3,...,m-1)
在发生冲突进行重新定址的过程中会导致后面不是同义词的关键字( f(keyi) ≠ f(key2) )争夺一个地址的情况,这种现象为堆积。
  线性探测都是发生冲突后加上一个di然后取余数来寻找下一个空间地址,如果发生冲突的位置之前就有一个空位置,要找到这个空位置要循环效率就很低
  fi(key) = (f(key) + di) MOD m (di = 1^2,-1^2,2^2,...,q^2,-q^2,q≤m/2)
增加平方运算的目的是为了不让关键字都聚集在某一块区域,这种方法叫做二次探测法。
   在冲突时,对于位移量di采用随机函数计算得到。
  fi(key) = (f(key) + di) MOD m (di 是一个伪随机数列) 。 伪随机,只要随机种子相同,每次得到的数列都会是相同的。这就是随机探测法
再散列函数法
  fi(key) = RHi(key)  (i=1,2,...,k)
其中RHi就是不同的散列函数,每当发送冲突时,就换一个散列函数计算,总有一个可以解决冲突
链地址法:将所有关键字为同义词的记录存储在一个单链表中,这种链表叫做同义词子表,使用除留余数法,就不存在冲突的问题了,只是在链表中增加一个结点。
公共溢出区法:
  对所有冲突的关键字建立一个公共溢出区来存放
   
#include<iostream>
#include<stdlib.h>
#define max  ~( 1<<(sizeof(int)*8-1) )
using namespace std;
//散列函数采用除留余数法
//冲突解决采用开放定址法的线性探测法
int hashFunc(int key,int length);
int initHashTable(int* hashTable,int length);  //成功返回0,失败返回-1
int hashInsert(int* hashTable,int key,int length);   //成功返回0,失败返回-1
static enum status{failture=-1,success=0} flag;
int hashSearch(int*hashTable,int key,int length); 
void test();
int main()
{
        test();
        system("pause");
}
int initHashTable(int* hashTable,int length)
{
        if(nullptr==hashTable || length<=0)
                return -1;
        for(int idx=0;idx!=length;++idx)
        {
                hashTable[idx] = max;
        }
        return 0;
}
int hashFunc(int key,int length)
{
        if(key==max||length<=0)
                return -1;
        return key % length;    //除留余数
}
int hashInsert(int* hashTable,int key,int length)
{
        if(nullptr==hashTable||length<=0)
                return -1;
        int hashAddr = hashFunc(key,length);
        if(-1==hashAddr)
                return -1;
        for(int idx=0;idx!=length;++idx)    //循环,最大哈希表长度
        {
                if(hashTable[hashAddr]!=max)    //冲突
                        hashAddr = (hashAddr+1) % 12;   //开放定址法的线性探测法,查找下一个可存放数据的空间
                else
                        break;
        }
        if(max==hashTable[hashAddr])
        {
                hashTable[hashAddr] = key;
                return 0;
        }
        return -1;
}
void test()
{
        int arr[12] = {12,67,56,16,25,37,22,29,15,47,48,34};
        int* hashTable = new int[12]();
        int ret = initHashTable(hashTable,12);
        if(-1==ret)
                cout<<"initHashTable fail!"<<endl;
        cout<<"哈希表待插入元素:";
        for(int idx=0;idx!=12;++idx)
        {
                cout<<arr[idx]<<" ";
                hashInsert(hashTable,arr[idx],12);
        }
        cout<<endl;
        cout<<"哈希表:";
        for(int idx=0;idx!=12;++idx)
        {
                cout<<hashTable[idx]<<" ";
        }
        cout<<endl;
        cout<<"对应插入元素序列在哈希表查找元素:";
        for(int idx=0;idx!=12;++idx)
        {
                int ret = hashSearch(hashTable,arr[idx],12);
                if( ret==-1 && flag == failture)
                {
                        cout<<"search "<<arr[idx]<<" fail"<<endl;
                }
                cout<<hashTable[ret]<<" ";
        }
        cout<<endl;
        cout<<"查找1:"<<endl;
        int rett = hashSearch(hashTable,1,12);
        if( rett==-1 && flag == failture)
        {
                cout<<"search "<<1<<" fail"<<endl;
                return ;
        }
        cout<<hashTable[rett]<<endl;

}
int hashSearch(int*hashTable,int key,int length)
{
        flag = success;
        if(nullptr==hashTable||length<=0)
        {
                flag = failture;
                return -1;
        }
        int hashAddr = hashFunc(key,length);
        while(key!=hashTable[hashAddr])
        {
                hashAddr = (hashAddr+1) % length;
                if(max==hashTable[hashAddr] || hashAddr == hashFunc(key,length))  //如果探测到下一个地址为空,还没有找到,或者循环找了一遍又回到最开始的hashAddr
                {
                        flag = failture;
                        return -1;
                }
        }
        return hashAddr;
}

哈希表概念和实现,C/C++实现的更多相关文章

  1. 哈希表(hash table)基础概念

    哈希是什么 引入:我们在学习数组的时候,使用数组元素的下标值即可访问到该元素,所花费的时间是O(1),与数组元素的个数n没有关系,这就是哈希方法的核心思想. 哈希方法:以关键值K为自变量,通过一定的函 ...

  2. 什么叫哈希表(Hash Table)

    散列表(也叫哈希表),是根据关键码值直接进行访问的数据结构,也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表. - 数据结构 ...

  3. Berkeley DB的数据存储结构——哈希表(Hash Table)、B树(BTree)、队列(Queue)、记录号(Recno)

    Berkeley DB的数据存储结构 BDB支持四种数据存储结构及相应算法,官方称为访问方法(Access Method),分别是哈希表(Hash Table).B树(BTree).队列(Queue) ...

  4. 从HashMap透析哈希表

    ##扯数据结构 先看一下哈希表的概念: 哈希表是一种数据结构,它可以提供快速的插入操作和查找操作.第一次接触哈希表,他会让人难以置信,因为它的插入和删除.查找都接近O(1)的时间级别.用哈希表,很多操 ...

  5. 哈希表的C++实现(转)

    哈希表的几个概念: 映像:由哈希函数得到的哈希表是一个映像. 冲突:如果两个关键字的哈希函数值相等,这种现象称为冲突. 处理冲突的几个方法: 1.开放地址法:用开放地址处理冲突就是当冲突发生时,形成一 ...

  6. 散列表 (Hash table,也叫哈希表)

    散列表是根据关键字(Key value)而直接访问在内存存储位置的数据结构.也就是说,它通过把键值通过一个函数的计算,映射到表中一个位置来访问记录,这加快了查找速度.这个映射函数称做散列函数,存放记录 ...

  7. C#中哈希表与List的比较

    简单概念 在c#中,List是顺序线性表(非链表),用一组地址连续的存储单元依次存储数据元素的线性结构. 哈希表也叫散列表,是一种通过把关键码值映射到表中一个位置来访问记录的数据结构.c#中的哈希表有 ...

  8. 哈希,哈希表,哈希Map

    数组: 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二分查找时间复杂度小,为O(1):数组的特点是:寻址容易,插入和删除困难: 链表: 链表存储区间离散,占用内存比较宽松,故空间复杂 ...

  9. perl5 第九章 关联数组/哈希表

    第九章 关联数组/哈希表 by flamephoenix 一.数组变量的限制二.定义三.访问关联数组的元素四.增加元素五.创建关联数组六.从数组变量复制到关联数组七.元素的增删八.列出数组的索引和值九 ...

随机推荐

  1. js中use或者using方法

    看Vue.use方法,想起了以前工作中别人用过的use方法. var YANMethod = { using:function() { var a = arguments, o = this, i = ...

  2. angular2 学习笔记 (Typescript - Attribute & reflection & decorator)

    更新 : 2018-11-27 { date: Date } 之前好像搞错了,这个是可以用 design:type 拿到的 { date: Date | null } 任何类型一但配上了 | 就 de ...

  3. 最短路径遍历所有的节点 Shortest Path Visiting All Nodes

    2018-10-06 22:04:38 问题描述: 问题求解: 本题要求是求遍历所有节点的最短路径,由于本题中是没有要求一个节点只能访问一次的,也就是说可以访问一个节点多次,但是如果表征两次节点状态呢 ...

  4. Linux 各种软件的安装-tomcat8+JDK篇

    其实自己没搞过php,主要还是弄java,tomcat和java是必不可少的 首先安装JDK,注意linux和winodws的jdk完全不同,别下载错了. 先 yum update 把软件更新一下 j ...

  5. js新打开页面

    var a = document.createElement("a"); a.setAttribute("href", href); a.setAttribut ...

  6. Sereja and Two Sequences CodeForces - 425C (dp)

    大意: 给定序列$a,b$, 每次可以任取两个相同大小的$a_i,b_j$删除$a_i,b_j$左侧所有元素, 花费为e, 得分1, 最后结束时必须再花费之前删除元素的个数, 不得分. 初始能量$s$ ...

  7. thinkphp或者kohana 导入和读取文件

    1.无论是那个框架的导入,其实都是一样的原理的,但是首先我们要导入包,可能就这点不同. kohana的导入包的方法:require_once(Kohana::find_file('vendor','P ...

  8. virtualbox 中centOS在不能ssh

    这个重要跟虚拟机的网络设置有关系.废话不多说. 针对一个网卡的形式.可以如下进行配置 1.网络-- 连接方式还选择“网络地址转换(NAT)” 其他不变,展开高级,设置端口转发  主机IP设为本机IP, ...

  9. maven中 install的install:install的区别

    如果一个项目,你想安装jar包到本地仓库,可能会报The packaging for this project did not assign a file to the build artifact ...

  10. Vue keep-alive总结

    <keep-alive>是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM. <keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是 ...