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. iview使用的bug

    1.table中排序和render方法不能共存,解决方法:保留render,然后排序不用提供的排序,自己写事件.

  2. Axure 全局变量公式的使用和局部变量

    全局变量和全部变量的公式: 1.全局变量在当前用例所有的动作里面都有,都可以设置 2.全局变量的公式在中括号里面的变量可以运算,但是在中括号外面的变量只是起到连接的作用 局部变量: 局部变量只可以在当 ...

  3. if标签

    If标签如果php中if语句的作用,if是用于流程控制的. 在ThinkPHP中if标签也是用于流程控制的. If标签的语法格式: <if condition=’条件表达式’> 输出结果1 ...

  4. codeforces 578a//A Problem about Polyline// Codeforces Round #320 (Div. 1)

    题意:一个等腰直角三角形一样的周期函数(只有x+轴),经过给定的点(a,b),并且半周期为X,使X尽量大,问X最大为多少? 如果a=b,结果就为b 如果a<b无解. 否则,b/(2*k*x-a) ...

  5. django权限管理(一)

    权限:权限就是一个包含正则的url. Rbac 权限管理: Role-Based Access Control,基于角色的访问控制.用户通过角色与权限进行关联,一个用户可以有多个角色,一个角色可以有多 ...

  6. Linux系统起源及主流发行版

    Linux系统起源及主流发行版   本文首先介绍了三大服务器系统,然后介绍了Linux系统的出现背景.以及主要release版本,最后介绍了Linux的文件系统和目录结构.   服务器系统,即安装在服 ...

  7. Sonya and Matrix CodeForces - 1004D (数学,构造)

    http://codeforces.com/contest/1004/problem/D 题意:网格图给定到中心点的曼哈顿距离数组, 求该图n,m及中心点位置 首先可以观察到距离最大值mx一定在某个角 ...

  8. summary ranges leetcode java

    问题描述: Given a sorted integer array without duplicates, return the summary of its ranges. For example ...

  9. leetcode-algorithms-26 Remove Duplicates from Sorted Array

    leetcode-algorithms-26 Remove Duplicates from Sorted Array Given a sorted array nums, remove the dup ...

  10. js 动态添加 外部js css 到head标签

    function appendJQCDN() { var head = document.head || document.getElementsByTagName('head')[0]; var s ...