11.1 散列表
11.1.1 引子:散列的基本思路
C语言变量名必须:
- 先定义(或者声明)后使用
编译处理时,涉及变量及属性(如:变量类型)的管理:
- 插入:新变量定义(将变量名及其定义插到我们要管理的这个集合里面去)
- 查找:变量的引用(在编译的时候,会查找你使用的这个变量是否定义,在根据变量的类型去进行判别看能不能这么使用)
编译处理中对变量管理:
- 动态查找问题
利用查找树(搜索树)进行变量管理?
- 两个变量名(字符串)比较效率不高,因为变量名比较比整数的比较要复杂。
- 整数比较一次比较大小相等就行了,而变量是字符串,需要一个字符一个字符的比过去
- 是否可以先把字符串转换为数字再处理?这样就是进行比较两个数字的方式了,这就是散列查找
已知的几种查找方法:
- 顺序查找:要查找的放数组列表,从头到尾慢慢找,效率差
- 二分查找(静态查找):从小到大排好然后用二分查找这样,查找效率高
- 二叉搜索树,平衡二叉树
问题:如何快速搜索到需要的关键词?如果关键词不方便比较怎么办?
- 查找的本质:已知对象找位置
- 有序安排对象:全序(典型的二分查找,从小到大排好),半序(某些关键词之间存在一个秩序,例如查找树,任何一个结点都比左边左子树的所有结点要来得大,比右边右子树的所有结点要来得小,但并不像二分查找一样完全有序)
- 直接"算出"对象位置:散列
- 散列查找法的两项基本工作:
- 计算位置:构造散列函数确定关键词存储位置
- 解决冲突:应用某种策略解决多个关键词位置相同的问题
- 时间复杂度几乎是常量:O(1),即查找时间与问题规模无关!
- 散列如果在函数的计算方面还有冲突策略提得好,效率就会非常高
11.1.2 什么是散列表
类型名称:符号表(SymbolTable)
数据对象集:符号表是“名字(Name)-属性(Attribute)”对的集合。
操作集:Table∈SymbolTable,Name∈NameType,Attr∈AttributeType
1.SymbolTableInitializeTable(intTableSize )://表的初始化
创建一个长度为TableSize的符号表;
2.BooleanIsIn(SymbolTableTable,NameTypeName)://判别一个对象是不是在这个表里
查找特定的名字Name是否在符号表Table中;
3.AttributeTypeFind(SymbolTableTable,NameTypeName)://在表里查找属性
获取Table中指定名字Name对应的属性;
4.SymbolTableModefy(SymbolTableTable,NameTypeName,AttributeTypeAttr)://把表中属性改掉
将Table中指定名字Name的属性修改为Attr;
5.SymbolTableInsert(SymbolTableTable,NameTypeName,AttributeTypeAttr)://在表里插入一个新的对象
向Table中插入一个新名字Name及其属性Attr;
6、SymbolTableDelete(SymbolTableTable,NameTypeName)://从表里删除
从Table中删除一个名字Name及其属性。
主要操作:上面的3、5、6
装填因子:散列表在外面这里是个数组,这个数组被充满的程度
冲突的设计:采用二维数组,同一个地址的就放在同一行,有冲突的在所在那行后一列继续放,如图所示:
哈希函数设计:
哈希函数指将哈希表中元素的关键键值映射为元素存储位置的函数。
一般的线性表,树中,记录在结构中的相对位置是随机的,即和记录的关键字之间不存在确定的关系,因此,在结构中查找记录时需进行一系列和关键字的比较。这一类查找方法建立在“比较“的基础上,查找的效率依赖于查找过程中所进行的比较次数。 理想的情况是能直接找到需要的记录,因此必须在记录的存储位置和它的关键字之间建立一个确定的对应关系,使每个关键字和结构中一个唯一的存储位置相对应。
只要不冲突的话效率还是很高的
▣“散列(Hashing)”的基本思想是:
(1)以关键字key为自变量,通过一个确定的函数h(散列函数),计算出对应的函数值h(key),作为数据对象的存储地址。
(2)可能不同的关键字会映射到同一个散列地址上,即h(keyi)=h(keyj)(当keyi不等于keyj),称为“冲突(Collision)”。---需要某种冲突解决策略