数据结构与算法9:查找
文章目录
- 第七章 查找
- 7.1、查找的基本概念
- 7.2、线性表的查找
- 7.2.1、顺寻查找(线性查找)
- 7.2.2、折半查找(二分或对分查找)
- 查找过程:
- 折半查找算法:(非递归算法)
- 折半查找——递归算法
- 折半查找的性能分析——判定树
- 折半查找的性能分析——ASL
- 7.2.3、分块查找
- 分块查找(索引顺序查找)todo
- 分块查找性能分析
- 分块查找优缺点
- 查找方法比较
- 7.3、树表的查找
- 7.3.1、二叉排序树
- 二叉树排序树的操作——查找
- 算法:二叉排序树的递归查找
- 二叉排序树的操作——插入
- 二叉排序树的操作——生成
- 二叉排序树的操作——删除
- 7.3.2、平衡二叉树
- 1、平衡二叉树的定义
- 2、失衡二叉排序树的分析与调整
- 7.3.3、B-树
- 7.3.4、B+树
- 7.4、散列表的查找
- 7.4.1、散列表的基本概念
- 7.4.2、散列表的若干术语
- 7.4.3、散列函数的构造方法
- 7.4.4、散列表的查找
第七章 查找
知识回顾
7.1、查找的基本概念
问题:在哪里找?————查找表
查找表:是由统一类型的数据元素(或记录)构成的集合。由于“集合”中的数据元素之间存在着松散的关系,因此查找表是一种应用灵便的结构。
问题:什么查找?—————根据给定的某个值,在查找表中确定一个其关键子等于给定值的数据元素(或记录)
关键字:用来标识一个数据元素(或记录)的某个数据项的值,有主、次关键字之分。
● 主关键字:可唯一标识一个记录的关键字是主关键字;
● 次关键字:反之,用以识别若干记录的关键字是次关键字。
问题:查找成功否?————
查找—根据给定的某个值,在查找标中确定一个其关键字等于给定值的数据元素或(记录)
若查找表中存在这样的一个记录,则称“查找成功”:查找结构给出整个记录的信息,或指示该记录在查找表中的位置。
否则称“查找不成功”:查找结果给出“空记录”或“空指针”。
问题:查找目的是什么?
对查找表经常进行的操作:
● 1、查询某个“特定的”数据元素是否在查找表中;
● 2、检索某个“特定的”数据元素的各种属性;
● 3、在查找表中插入一个数据元素;
● 4、删除查找表中的某个数据元素。
问题:查找表怎么分类?
查找表可分为两类:
静态查找表:仅作“查询"(检索)操作的查找表。
动态查找表:作“插入”和“删除”操作的查找表。有时在查询之后,还需要将“查询”结果“不在查找表中”的数据元素插入到查找表中;或者,从查找表中删除其“查询”结果为“在查找表中”的数据元素,此类表为动态查找表。
问题:如何评价查找算法? ———查找算法的评价指标:
关键字的平均比较次数,也称平均查找长度ASL(Average Search Length)
ASL =∑i=0npici\sum_{i=0}^n p_ic_i∑i=0npici (关键字的比较次数的期望值)
n:记录的个数
pi:查找第i个记录的概率(通常认为pi = 1/n)
ci:找到第i个记录所需的比较次数
问题:查找过程中我们要研究什么?————研究查找表的各种组织方法及其查找过程的实施。
查找的方法取决于查找表的结构,即表中数据元素是依何种关系组织在一起的。
由于对查找表来说,在集合中查询或检索一个“特定的”数据元素时,若无规律可循,只能对集合中的元素——加以辨认直到找到为止。
而这样的“查询”或“检索”是任何计算机应用系统中使用频度都很高的操作,因此设法提高查找表的查找效率,是本章讨论问题的出发点。
为提高查找效率,一个办法就是在构造查找表时,在集合中的数据元素之间认为地加上某种确定的约束关系。
7.2、线性表的查找
7.2.1、顺寻查找(线性查找)
应用范围:
● 顺表表或线性链表表示的静态查找表
● 表内元素之间无序
顺序表的表示:
数据元素类型定义:
typedef struct{KeyType key; //关键字域...... //其他域
}ElemType;typedef struct{ElemType *R; //表基址int length; //表长
}SSTable; //Sequential Search Table
SSTable ST; //定义顺序表
在顺序表ST中从查找值为key的数据元素(从最后一个元素开始比较)
算法:
int Search_Seq(SSTable ST,KeyType key){//若成功返回其位置信息,否则返回0for(i = ST.length;i >= 1; --i){if(ST.R[i].key == key) return i;else return 0;}}
算法的其他形式:
int Search_Seq(SSTable ST,KeyType key){for(i = ST.length;ST.R[i].key != key;--i){if(i <= 0) break;if(i > 0) return i;else return 0;}
}int Search_Seq(SSTable ST,KeyType key){for(i = ST.length; ST.R[i].key != key && i > 0; --i);//注意此处的分号if(i > 0) return i;else return 0;
}
问题:每次执行一次循环都要进行两次比较,是否能改进?————改进:设置监视哨的顺序查找
改进:把待查关键字key存入表头(“哨兵”,“监视哨”),从后往前逐个比较,可免去查找过程中每一步都要检测是否查找完毕,加快速度。
int Search_Seq(SSTable ST,KeyType key){ST.R[0].key = key;//把要查找的值存进下标为0的位置for(i = ST.length;ST.R[i].key != key; --i);return i;
}
当ST.length 较大时,此改进能使进行一次查找所需的平均时间几乎减少一半。
顺序查找的性能分析
时间效率分析
比较次数与key位置有关:
● 查找第i个元素,需要比较n-i+1次
● 查找失败,需比较n+1次。
时间复杂度:O(n):查找成功时的平均查找长度,设表中各记录查找概率相等。ASLs(n)=(1+2…+n)/n = (n+1)/2
空间复杂度:一个辅助空间——O(1);
讨论:todo
1、记录的查找概率不相等时如何提高查找效率?(查找表存储记录原则——按查找概率高低存储:)
● 1)查找概率越高,比较次数越少;
● 2)查找概率越低,比较次数较多。
2、记录的查找概率无法测定时如何提高查找效率?(方法——按查找概率动态调整记录顺序:)
● 1)在每个记录中设一个访问频度域;
● 2)始终保持记录按非递增有序的次序排列;
● 3)每次查找后均将刚查到的记录直接移至表头。
顺序查找的特点
优点:算法简单,逻辑次序无要求,且不同存储结构均适用。
缺点:ASL太长,时间效率太低。
7.2.2、折半查找(二分或对分查找)
折半查找:每次将待查记录所在区间缩小一半。
查找过程:
个人总结:
● 先确定最大的high和最小的low,然后根据最大的和最小的数组下标计算出两者中间的mid
● 中间的mid的确认后,mid所在的值立马与要查找到的key值比较大小,
○ 若是key<mid,low就移动mid后面一格;
○ 若是key>mid,high就移动mid前面一格;
● 最后依次比较下去,若是key=mid就是找到了,若是途中若high<low就结束,表示所找key不存在。
注意:mid移动是依靠的是high和low的下标遵守取整函数移动。low和high移动依靠的是mid与key值(存储在数组中的元素数据,而不是数组下标)的比较移动。
折半查找算法:(非递归算法)
● 设表长为n、low、high和mid分别指向待查元素所在区间的上界、下界和中点,key为给定的要查找的值:
● 初始时,令low=1,high=n,mid=|(low +high)/2)|
● 让k与mid指向的记录比较
○ 若key==R[mid].key,查找成功
○ 若key<R[mid].key,则high=mid-1
○ 若key>R[mid].key,则low = mid+1
● 重复上述操作,直到low>high时,查找失败
int Search_Bin(SSTable ST,KeyType key){//置区间初值low = 1;high = ST.length;while(low <= high){mid = (low + high)/2;if(ST.R[mid].key == key){ return mid;//找到待查元素}//缩小查找区间else if(key < ST.R[mid].key){high = mid -1 ; //继续在前半区间进行查找} else{ low = mid + 1; //继续在后半区间进行查找}return 0; //顺序表中不存在待查元素
}
折半查找——递归算法
int Search_Bin(SSTable ST,keyType key,int low, int high){if(low > high)return 0;mid = (low + high)/2;if(key == ST.elem[mid].key) return mid;else if(key < ST elem[mid].key)......//递归,在前半区间进行查找else ....
.. //递归,在后半区间进行查找
}
折半查找的性能分析——判定树
练习:
假定每个元素的查找概率相等,求查找成功时的平均查找长度。
ASL = 1/11*(11 + 22 + 43 + 44) = 33/11 = 3 (红色字体为查找元素,乘以后面的查找次数)乘以总共查找元素的的倒数便是查找平均长度。
折半查找的性能分析——ASL
平均查找长度ASL(成功时):
设表长n=2h−1n = 2^h -1n=2h−1则h=log2(n+1)h = log_2(n+1)h=log2(n+1)(此时,判定树为深度 = h 的满二叉树),且表中记录的查找概率相等:Pi=1/nP_i =1/nPi=1/n。
则
折半查找优点:效率比较顺序查找高。空间性能是对数级别
折半查找缺点:只适用于有序表,且限于顺序存储结构(对线性链表无效)。
7.2.3、分块查找
分块查找(索引顺序查找)todo
条件:
● 1、将表分成几块,且表或者有序,或者分块有序;若i<j,块中所有记录的关键字均大于第i块中的最大关键字。
● 2、建立“索引表”(每个结点含有最大关键字域和指向本块第一个结点的指针,且按关键字有序)。
查找过程:先确定待查记录所在块(顺序或折半查找),索引表再在块内查找(顺序查找)。
分块查找性能分析
分块查找优缺点
● 优点:插入和删除比较容易,无需进行大量移动。
● 缺点:要增加一个索引表的储存空间并对初始索引表进行排序运算。
● 适用情况:如果线性表既要快速查找又经常动态变化,则可采用分块查找。
查找方法比较
顺序查找 | 折半查找 | 分块查找 | |
---|---|---|---|
ASL | 最大 | 最小 | 中间 |
表结构 | 有序表、无序表 | 有序表 | 分块有序 |
存储结构 | 顺序表、线性链表 | 顺序表 | 顺序表、线性链表 |
7.3、树表的查找
当表插入、删除操作频繁时,为维护表的有序性,需要移动表中很多记录。——改用动态查找表:几种特殊的树。 表结构在查找过程中动态生成 对于给定值key,若表中存在,则成功返回;否则插入关键字等于key的记录。
几种特殊的树为
● 二叉排序树(重点)
● 平衡二叉树(重点)
● 红黑树
● B-树
● B+树
● 键树
7.3.1、二叉排序树
二叉排序树(Binary Sort Tree)又称为二叉搜索树、二叉查找树,
定义:
二叉排序树或是空树,或是满足如下性质的二叉树:
(1)若其左子树非空,则左子树上所有结点的值均小于根结点的值;
(2)若其右子树非空,则右子树上所有结点的值均大于等于根结点的值;
(3)其左右子树本身又各是一棵二叉树排序树
思考:todo
● 中序遍历二叉排序树
● 结果有什么规律?
二叉排序树的性质:
中序遍历非空的二叉树排序树所得到的数据元素序列是一个按关键字排列的递增有序序列。
二叉树排序树的操作——查找
● 若查找的关键字等于根结点,成功
● 否则
○ 若小于根节点,查其左子树
○ 若大于根结点,查其右子树
● 在左子树上的操作类似
二叉树排序树的存储结构todo
typedef struct{KeyType key; // 关键字项InfoType otherinfo; //其他数据域
}ElemType;typedef struct BSTNode{ElemType data; //数据域struct BSTNode *lchild,*rchild;//左右孩子指针
}BSTNode,*BSTree;
BSTree T;//定义二叉排序树T
算法:二叉排序树的递归查找
算法思想:
(1)若二叉排序树为空,则查找失败,返回空指针。
(2)若二叉排序树非空,将给定值key与根结点的关键字T->data.key进行比较:
● 若key等于T -> data.key,则查找成功,返回根结点地址;
● 若key小于T -> data.key,则进一步查找左子树;
● 若key大于T ->data.key,则进一步查找右子树。
算法描述:
BSTree SearchBST(BSTree T,KeyType key){if((!T)||key == T -> data.key) return T;else if(key < T -> data.key) return SearchBST(T -> lchild,key);//在左子树中继续查找else return SearchBST(T -> rchild,key);//在右子树中继续查找
}//SearchBST
二叉排序树的查找分析
二叉树排序树上查找某关键字等于给定值的结点过程,其实就是走了一条从根到该结点的路径。
比较的关键字次数 = 此节点所在的层次数 最多的比较次数 = 树的深度 todo
二叉排序树的平均查找长度:
含有n个结点的二叉排序树的平均查找长度和树的形态有关
最好的情况:初始序列{45,24,53,12,37,93} ASL = log2(n+1)−1log_2(n+1)-1log2(n+1)−1;树的深度为:|log2nlog_2nlog2n|+1;与折半查找中的判定树相同。(形态比较均衡):O(log2nlog_2nlog2n)
最坏的情况:初始序列{12,24,37,45,53,93}插入的n个元素从一开始就有序——变成单枝树的形态!此时树的深度为n,ASL=(n+1)/2查找效率与顺序查找情况相同:O(n)
问题:如何提高形态不均衡的二叉排序树的查找效率?
解决办法:做"平衡化"处理,即尽量让二叉树的形状均衡!
二叉排序树的操作——插入
● 若二叉排序树为空,则插入结点作为根结点插入到空树中
● 否则,继续在其左、右子树上查找
○ 树中已有,不再插入
○ 树中没有
■ 查找直至某个叶子结点的左子树或右子树为空为止,则插入结点应为该叶子结点的左孩子或右孩子。
注意:插入的元素一定在叶子结点上。
二叉排序树的操作——生成
从空树出发,经过一系列的查找、插入操作之后,可生成一棵二叉排序树。
例:设查找的关键字序列为{45,24,53,45,12,24,90},可生成二叉排序树如下
一个无序序列可通过构造二叉排序树而变成一个有序序列。构造树的过程就是对无序序列进行排序的过程。
插入的结点均为叶子结点,故无需移动其他结点。相当于在有序序列上插入记录而无需移动其他记录。
但是:关键字的输入顺序不同,建立的不同二叉排序树。
不同插入次序的序列生成不同形态的二叉排序树
二叉排序树的操作——删除
从二叉排序树中删除一个结点,不能把以该结点为根的子树都删去,只能删掉该结点,并且还应保证删除后所得的二叉树仍然满足二叉排序树的性质不变。
由于中序遍历二叉排序树可以得到一个递增有序的序列。那么,在二叉排序树中删去一个结点相当于删去有序序列中的一个结点。
● 将因删除结点而断开的二叉链表重新链接起来
● 防止重新链接后树的高度增加。
(1)被删除的结点是叶子结点:直接删去该节点。例如:
(2)被删除的结点只有左子树或者只有右子树,用其左子树或者右子树替换它(结点替换)。
其双亲结点的相应指针域的值改为”指向被删除结点的左子树或右子树“。
(3)被删除的结点既有左子树,也有右子树。
● 以其中序前趋值替换之(值替换),然后再删除该前趋结点。前趋是左子树中最大的结点。
● 也可以用其后继替换之,然后再删除后继结点。后继是右子树中最小的结点。
注意:是以前趋值替换,还是以后继值替换,取决于能那种方法能降低树的深度。
7.3.2、平衡二叉树
1、平衡二叉树的定义
平衡二叉树(balanced binary tree)
● 又称AVL树(Adelson-Velskii and Landis)。
● 一棵平衡二叉树或者是空树,或者是具有下列性质的二叉排序树:
○ a.左子树与右子树的高度之差的绝对值小于等于1;
○ b.左子树和右子树也是平衡二叉排序树。
为了方便起见,给每个结点附加一个数字,给出该结点左子树与右子树的高度差。这个数字称为结点的平衡因子(BF)。
平衡因子 = 结点左子树的高度 - 结点右子树的高度。
根据平衡二叉树的定义,平衡二叉树上所有的平衡因子只能是-1,0,或1。
对于一棵有n个结点的AVL树,其高度保持在O(log2nlog_2nlog2n)数量级,ASL也保持在O(log2nlog_2nlog2n)量级。
2、失衡二叉排序树的分析与调整
当我们在一个平衡二叉排序树上插入一个结点时,有可能导致失衡,即出现平衡因子绝对值大于1的结点,如:2、-2。
如果在一棵AVL树中插入一个新结点后造成失衡,则必须重新调整树的结构,使之恢复平衡。
平衡调整的四种类型:‘
A:失衡结点,不止一个失衡结点时,为最小失衡子树的根结点
B:A结点的孩子,C结点的双亲。
C:插入新结点的子树。
调整原则:判断大小,把中间的作为根结点
● 降低高度
● 保持二叉排序树的性质
(1)LL型调整
调整过程:α(阿尔法)、β(贝塔)、γ(伽马)
● B结点带左子树α
● A结点成为B的右孩子
● 原来B结点的右子树β作为A的左子树
AVL树LL调整——例子:
(2)RR型调整
RR型调整过程:
● B结点带右子树β一起升
● A结点成为B的左孩子
● 原来B结点的左孩子α作为A的右子树
在这里插入图片描述
AVL树RR调整——例子:
(3)LR型调整
调整过程
● C结点穿过A、B结点上升
● B结点成为C的左孩子,A结点成为C的右孩子
● 原来C结点的左子树β作为B的右子树;原来C结点的右子树γ作为A的左子树。
AVL树LR调整——例子:
(4)RL型调整
AVL树调整——例子:
例题:输入关键字序列(16、3、7、11、9、26、18、14、15),给出构造AVL树的步骤。
7.3.3、B-树
7.3.4、B+树
7.4、散列表的查找
7.4.1、散列表的基本概念
基本思想:记录的存储位置与关键字之间存在对应关系
对应关系——hash函数
Hash:哈希 翻译为:散列、杂凑——————散列表
例如要查找2001011180216的信息,可直接访问V[16]
根据散列函数H(key)=k,查找key=9,则访问H(9) = 9号地址,若内容为9则成功;若查不到,则返回一个特殊值,如空指针或空记录。
优点:查找效率高。 缺点:空间效率低。
7.4.2、散列表的若干术语
散列方法(杂凑法)
选取某个函数,依次函数按关键字计算元素的存储位置,并按此存放;查找时,由同一个函数对给定值k计算地址,将k与地址单元中元素关键码进行比较,确定查找是否成功。
散列函数(杂凑函数):散列方法中使用的转换函数
散列表(杂凑表):按上述思想构造的表
散列函数:H(key)=k
冲突:不同的关键码映射到同一个散列地址key1 ≠key2,但是H(key1) = H(key2)
例:有6个元素的关键码分别为:(25、21、39、9、23、11)
● 选取关键码与元素位置间的函数为H(k) =k mod 7
● 地址编号从0-6.
通过散列函数对6个元素建立散列表:
同义词:具有相同函数值的多个关键字
7.4.3、散列函数的构造方法
散列存储
选取某个函数,依该函数按关键字计算元素的存储位置 Loc(i) =H(keyi)
冲突 不同的关键码映射到同一个散列地址 key1≠key2,但是H(key1)=H(key2)
在散列查找方法中,冲突是不可能避免的,只能尽可能减少。——怎么减少?
使用散列表要解决好两个问题:
1、构造好的散列函数
● a.所选函数尽可能简单,以便提高转换速度;
● b.所选函数对关键码计算处的地址,应在散列地址集中致均匀分布,以减少空间浪费。
2、制定一个好的解决冲突的方案
查找时,如果从散列函数计算出的地址中查不到关键码,则应当依据冲突的规则,有规律地查询其他相关单元。
构造散列函数考虑的因素
● 1、执行速度(即计算散列函数所需时间);
● 2、关键字的长度;
● 3、散列表的大小;
● 4、关键字的分布情况;
● 5、查找频率。
根据元素集合的特性构造
● 要求1:n个数据原仅占用n个地址,虽然散列查找是以空间换时间,但仍希望散列的地址空间尽量小。
● 要求2:无论用什么方法存储,目的都是均匀地存放元素,以避免冲突。
散列函数的构造方法:
1、直接定址法 | 2、数字分析法 | 3、平方取中法 |
---|---|---|
4、折叠法 | 5、除留余数法(最常用) | 6、随机数法 |
直接定址法
Hash(key) = a.key + b(a,b为常数)
优点:以关键码key的某个线性函数值为散列地址,不会产生冲突。
缺点:要占用连续地址空间,空间效率低。
例如:{100,300,500,700,800,900},散列函数Hash(key) = key/100(a = 1/100,b =0)
除留余数法
Hash(key) = key mod p(p是一个整数)
关键:如何选取合适的p?
技巧:设表长为m,取p<=m 且为质数
例:{15,23,27,38,53,61,70}
散列函数 Hash(key)= key mod 7
处理冲突的方法:
1、开放定址法(开地址法):重点 | 3、再散列法(双散列法) |
---|---|
2、锁地址法(拉链法):重点 | 4、建立一个公共溢出区 |
1、开放地址法(开地址法)
基本思想:有冲突时就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总恩能够找到,并将数据元素存入。
例如:用留余数法 Hi=(Hash(key)+di)modmH_i = (Hash(key)+d_i)mod mHi=(Hash(key)+di)modm di为增量序列
常用方法:
线性探测法 | di为1,2,...,m−1线性序列d_i 为1,2,... ,m-1线性序列di为1,2,...,m−1线性序列 |
---|---|
二次探测法 | di为12,−12,22,−22,...,q2二次序列d_i为1^2,-1^2,2^2,-2^2,...,q^2二次序列di为12,−12,22,−22,...,q2二次序列 |
伪随机探测法 | di为伪随机数列d_i为伪随机数列di为伪随机数列 |
线性探测法
Hi=(Hash(key)+di)modm(1≤i<m)H_i = (Hash(key)+d_i)mod m (1≤i<m)Hi=(Hash(key)+di)modm(1≤i<m)其中:m为散列表长度,did_idi为增量序列1,2,…,m-1,且did_idi=i(注意:一旦冲突,就找下一个地址,直到找到空地址存入)
例如:关键码集为{47,7,29,11,16,92,22,8,3},散列为m =11;散列函数为Hash(key)=key mod 11;拟用线性探测法…冲突。建散列表如下:
解释:
● ①47、7均是有散列函数得到的没有冲突的散列地址;
● ②Hash(29) = 7,散列地址有冲突,需寻找下一个空的散列地址:由H1=(Hash(29)+1)mod11=8H_1=(Hash(29)+1)mod 11=8H1=(Hash(29)+1)mod11=8,散列地址8为空,因此将29存入。
● ③11、16、92均是由散列函数得到拍的没有冲突的散列地址;
● ④另外,22、8、3同样在散列地址上有冲突,也是有H1H_1H1找到空的散列地址的。
所以,平均查找长度ASL = (1+2+1+1+1+4+1+1+2+2)/9=1.67
二次探测法
关键码集为{47,7,29,11,16,92,22,8,3},
设:散列函数为Hash(key) = key mod 11Hi=(Hash(key)+di)modmH_i=(Hash(key)+d_i)mod mHi=(Hash(key)+di)modm
其中:m为散列表长度,m要求是某个4k+3的质数;di为增量序列12,−12,22,−22,...,q21^2,-1^2,2^2,-2^2,...,q^212,−12,22,−22,...,q2
伪随机探测法
Hi=(Hash(key)+di)modm(1≤i<m)H_i=(Hash(key)+d_i)mod m(1 ≤i<m)Hi=(Hash(key)+di)modm(1≤i<m)
其中:m为散列表长度,di为伪随机数
2 、链地址法(拉链法)
基本思想:相同散列地址的记录链成一单链表
m个散列地址就设m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构。
例如:一组关键字为{19.14,23,1,68,20,84,27,55,11,10,79},
散列函数为Hash(key)= key mod 13
链地址法建立散列表步骤
● Step1:取数据元素的关键字key,计算其散列函数值(地址)。若该地址对应的链表为空,则将该元素插入次链表;否则执行Step2解决冲突。
● Step2:根据选择的冲突处理方法,计算关键字key的下一个存储地址。若该地址对应的链表不为空,则利用链表的前插法或后插法将该元素插入次链表。
链地址法的优点:
● 非同义词不会冲突,无“聚集”现象
● 链表上结点空间动态申请,更适合于表长不确定的情况
3 、再散列法(双散列函数法)
4、建立一个公共溢出区
7.4.4、散列表的查找
给定值查找值k,查找过程:
例题
已知一组关键字(19,14,23,1,68,20,84,27,55,11,10,79),散列函数为:H(key) = key Mod 13,散列表长m=16,设每记录的查找概率相等。
(1)用线性探测再链列处理冲突,即Hi=(H(key)+di) mod m
(2)用链地址法处理冲突
关键字(19,14,23,1,68,20,84,27,55,11,10,79)
思考:对于关键字集(19,14,23,1,68,20,84,27,55,11,10,79), n = 12
散列表查找效率分析
使用平均查找长度ASL来衡量查找算法,ASL取决于
● 散列函数
● 处理 冲突的方法
● 散列表的装填因子α
装填因子α=(表中填入的记录数哈希表的长度)α = \begin{pmatrix} 表中填入的记录数\\ \hline 哈希表的长度\\ \end{pmatrix}α=(表中填入的记录数哈希表的长度),α越大,表中记录数越多,说明表装得越满,发生冲突得可能性就越大,查找时比较次数就越多。
ASL与装填因子α有关!既不是严格得O(1),也不是O(n)
● ASL≈1+(α2)ASL ≈ 1+ \begin{pmatrix} α\\ \hline 2\\ \end{pmatrix}ASL≈1+(α2)——————————拉链法
● ASL≈(12)[1+(11−α)]ASL ≈ \begin{pmatrix} 1\\ \hline 2\\ \end{pmatrix} [1+ \begin{pmatrix} 1\\ \hline 1-α\\ \end{pmatrix}]ASL≈(12)[1+(11−α)]——————线性探测法
● ASL≈−(1α)ln(1−α)ASL ≈ - \begin{pmatrix} 1\\ \hline α\\ \end{pmatrix}ln(1-α)ASL≈−(1α)ln(1−α)———————随机探测法
几点结论
● 散列表技术具有很好得平均性能,优于一些传统得技术
● 链地址法优于开地址法
● 除留余数法作散列函数优于其他类型函数