数据结构学习笔记07散列查找(哈希表)
2016-05-16 13:12
656 查看
1.散列表(Hash)
查找的本质: 已知对象找位置。
有序安排对象:全序、半序
直接“算出”对象位置:散列
时间复杂度几乎是常量:O(1),即查找时间与问题规模无关
散列查找法的两项基本工作:
计算位置:构造散列函数确定关键词存储位置;
解决冲突:应用某种策略解决多个关键词位置相同的问题
散列(Hashing) 的基本思想是:
①以关键字key为自变量,通过一个确定的函数 h(散列函数),计算出对应的函数值h(key),作为数据对象的存储地址。
②可能不同的关键字会映射到同一个散列地址上,即h(keyi) = h(keyj)(当keyi ≠keyj),称为“冲突(Collision)”。
----需要某种冲突解决策略
2.散列函数的构造方法
散列函数两个关键:
①计算简单,以便提高转换速度;
②关键词对应的地址空间分布均匀,以尽量减少冲突。
数字关键词的散列函数构造
①直接定址法
取关键词的某个线性函数值为散列地址,即
h(key) = a * key + b (a、b为常数)
②除留余数法
散列函数为:h(key) = key mod p (一般p取素数)
③数字分析法
分析数字关键字在各位上的变化情况,取比较随机的位作为散列地址
Eg:取11位手机号码key的后4位作为地址:
散列函数为:h(key) = atoi(key+7) (char *key)
④折叠法
把关键词分割成位数相同的几个部分,然后叠加
Eg: 56793542
542
793
+ 056
———
1391
h(56793542) = 391
⑤平方取中法
Eg: 56793542
56793542
x 56793542
—————————
3225506412905764
h(56793542) = 641
字符关键词的散列函数构造
①一个简单的散列函数——ASCII码加和法
对字符型关键词key定义散列函数如下:
h(key) = (Σkey[i]) mod TableSize
②简单的改进——前3个字符移位法(易造成空间浪费)
h(key)=(key[0]*27^2 + key[1]*27 + key[2]) mod TableSize
③好的散列函数——移位法
涉及关键词所有n个字符,并且分布得很好:
分离链接法
查找的本质: 已知对象找位置。
有序安排对象:全序、半序
直接“算出”对象位置:散列
时间复杂度几乎是常量:O(1),即查找时间与问题规模无关
散列查找法的两项基本工作:
计算位置:构造散列函数确定关键词存储位置;
解决冲突:应用某种策略解决多个关键词位置相同的问题
散列(Hashing) 的基本思想是:
①以关键字key为自变量,通过一个确定的函数 h(散列函数),计算出对应的函数值h(key),作为数据对象的存储地址。
②可能不同的关键字会映射到同一个散列地址上,即h(keyi) = h(keyj)(当keyi ≠keyj),称为“冲突(Collision)”。
----需要某种冲突解决策略
2.散列函数的构造方法
散列函数两个关键:
①计算简单,以便提高转换速度;
②关键词对应的地址空间分布均匀,以尽量减少冲突。
数字关键词的散列函数构造
①直接定址法
取关键词的某个线性函数值为散列地址,即
h(key) = a * key + b (a、b为常数)
②除留余数法
散列函数为:h(key) = key mod p (一般p取素数)
③数字分析法
分析数字关键字在各位上的变化情况,取比较随机的位作为散列地址
Eg:取11位手机号码key的后4位作为地址:
散列函数为:h(key) = atoi(key+7) (char *key)
④折叠法
把关键词分割成位数相同的几个部分,然后叠加
Eg: 56793542
542
793
+ 056
———
1391
h(56793542) = 391
⑤平方取中法
Eg: 56793542
56793542
x 56793542
—————————
3225506412905764
h(56793542) = 641
字符关键词的散列函数构造
①一个简单的散列函数——ASCII码加和法
对字符型关键词key定义散列函数如下:
h(key) = (Σkey[i]) mod TableSize
②简单的改进——前3个字符移位法(易造成空间浪费)
h(key)=(key[0]*27^2 + key[1]*27 + key[2]) mod TableSize
③好的散列函数——移位法
涉及关键词所有n个字符,并且分布得很好:
//分离链接法 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <math.h> using namespace std; #define MAXTABLESIZE 100000 /* 允许开辟的最大散列表长度 */ #define KEYLENGTH 15 /* 关键词字符串的最大长度 */ typedef char ElementType[KEYLENGTH+1]; /* 关键词类型用字符串 */ typedef int Index; /* 散列地址类型 */ /******** 以下是单链表的定义 ********/ typedef struct LNode *PtrToLNode; struct LNode { ElementType Data; PtrToLNode Next; }; typedef PtrToLNode Position; typedef PtrToLNode List; /******** 以上是单链表的定义 ********/ typedef struct TblNode *HashTable; /* 散列表类型 */ struct TblNode { /* 散列表结点定义 */ int TableSize; /* 表的最大长度 */ List Heads; /* 指向链表头结点的数组 */ }; int NextPrime( int N ) { /* 返回大于N且不超过MAXTABLESIZE的最小素数 */ int i, p = (N%2)? N+2 : N+1; /*从大于N的下一个奇数开始 */ while( p <= MAXTABLESIZE ) { for( i=(int)sqrt(p); i>2; i-- ) if ( !(p%i) ) break; /* p不是素数 */ if ( i==2 ) break; /* for正常结束,说明p是素数 */ else p += 2; /* 否则试探下一个奇数 */ } return p; } HashTable CreateTable( int TableSize ) { HashTable H; int i; H = (HashTable)malloc(sizeof(struct TblNode)); H->TableSize = NextPrime(TableSize);/* 保证散列表最大长度是素数 */ H->Heads = (List)malloc(H->TableSize*sizeof(struct LNode));/* 以下分配链表头结点数组 */ /* 初始化表头结点 */ for( i=0; i<H->TableSize; i++ ) { H->Heads[i].Data[0] = '\0'; H->Heads[i].Next = NULL; } return H; } Index Hash(ElementType Key, int TableSize ) { return (*Key - 'a') % TableSize; } Position Find( HashTable H, ElementType Key ) { Position P; Index Pos; Pos = Hash( Key, H->TableSize ); /* 初始散列位置 */ P = H->Heads[Pos].Next; /* 从该链表的第1个结点开始 */ /* 当未到表尾,并且Key未找到时 */ while( P && strcmp(P->Data, Key) ) P = P->Next; return P; /* 此时P或者指向找到的结点,或者为NULL */ } bool Insert( HashTable H, ElementType Key ) { Position P, NewCell; Index Pos; P = Find( H, Key ); if ( !P ) { /* 关键词未找到,可以插入 */ NewCell = (Position)malloc(sizeof(struct LNode)); strcpy(NewCell->Data, Key); Pos = Hash( Key, H->TableSize ); /* 初始散列位置 */ /* 将NewCell插入为H->Heads[Pos]链表的第1个结点 */ NewCell->Next = H->Heads[Pos].Next; H->Heads[Pos].Next = NewCell; return true; } else { /* 关键词已存在 */ printf("键值已存在"); return false; } } void DestroyTable( HashTable H ) { int i; Position P, Tmp; /* 释放每个链表的结点 */ for( i=0; i<H->TableSize; i++ ) { P = H->Heads[i].Next; while( P ) { Tmp = P->Next; free( P ); P = Tmp; } } free( H->Heads ); /* 释放头结点数组 */ free( H ); /* 释放散列表结点 */ } int main() { HashTable hash; hash = CreateTable(5); //real size 7: 0 1 2 3 4 5 6 Insert( hash, "a" ); Insert( hash, "b" ); Insert( hash, "c" ); Insert( hash, "d" ); Insert( hash, "e" ); Insert( hash, "h" ); Insert( hash, "g" ); return 0; }
分离链接法
相关文章推荐
- 数据结构习题 (整理中)
- 理解红黑树
- 数据结构之魔方程序
- C++数据结构-单向列表如何操作
- C 侵入式数据结构
- 数据结构实验之链表三:链表的逆置
- 包含min函数的最小栈
- 数据结构实验之链表二:逆序建立链表
- 数据结构实验之链表一:顺序建立链表
- 跟着郝斌学数据结构(08)——递归
- 数据结构上机测试2-2:单链表操作B
- 共同学习Java源代码--数据结构--ArrayList类(二)
- 跟着郝斌学数据结构(07)——队列(操作及应用)
- 跟着郝斌学数据结构(06)——队列(数组队列基本的一些问题)
- [notes] 可持久化数据结构学习笔记
- 数据结构上机测试2-1:单链表操作A
- 数据结构,二叉树已知后续中序,建树,层次遍历;
- 数据结构-平衡二叉树(AVL Tree)
- 跟着郝斌学数据结构(06)——队列(链式队列)
- 《数据结构》使用数组实现数制的转换