查找Search

基本概念

(1)在哪里找? 查找表

查找表是由同一类型的数据元素(或记录)构成的集合。由于"集合"中的数据元素之间存在着松散的关系,因此查找表是一种应用灵便的结构。

(2)什么是查找?

  根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素或(记录) 。

关键字:用来标识一个数据元素(或记录)的某个数据项的值。

主关键字:可唯一地标识一个记录的关键字是主关键字;

次关键字:反之,用以识别若干记录的关键字是次关键字。

(3)查找成功否?

  查找结果给出整个记录的信息,或指示该记录在查找表中的位置。否则称“查找不成功”:查找结果给出“空记录”或“空指针”。

(4)查找的目的?

对查找表经常进行的操作:

1、查询某个“特定的”数据元素是否在查找表中。

2、检索某个"特定的”数据元素的各种属性。

3、在查找表中插入一个数据元素。

4、删除查找表中的某个数据元素。

(5)查找表怎么分类?

静态查找表:仅作“查询”和 “检索” 操作的查找表。

动态查找表:作“插入"和"删除”操作的查找表。有时在查询之后,还需要将"查询”结果为“不在查找表中”的数据元素插入到查找表中。或者,从查找表中删除其"查询”结果为"在查找表中”的数据元素,此类表为动态查找表。

(6)如何评价查找?

关键字的平均比较次数:平均查找长度(ASL)ASL=\sum PiCi(关键字比较的期望值)

n:记录的个数,pi:查找第i个记录的概率(通常认为pi=1/n ),ci:找到第i个记录所需的比较次数。

(7)查找过程中我们要研究什么?

  查找的方法取决于查找表的结构,即表中数据元素是依何种关系组织在一起的。

  由于对查找表来说,在集合中查询或检索一个"特定的”数据元素时,若无规律可循,只能对集合中的元素一一加以辨认直至找到为止。

  而这样的"查询” 或"检索"是任何计算机应用系统中使用频度都很高的操作,因此设法提高查找表的查找效率,是本章讨论问题的出发点。

  为提高查找效率,一个办法就是在构造查找表时,在集合中的数据元素之间人为地加上某种确定的约束关系

  研究查找表的各种组织方法及其查找过程的实施。

线性表的查找

顺序查找(线性查找)

应用范围:顺序表或者线性链表表示的静态查找表,表内元素无序。

数据元素定义:

 在顺序表ST中查找值为key的数据元素:我们从最后一个元素开始:

改进:把待查关键字key存入表头(“哨兵”),从后面依次逐个比较,可以免去查找过程中每一步都要检测是否查找完毕,加快速度。 

当ST.length较大时,此改进能使进行一次查找所需的平均时间几乎减少一半。

查找效率:

时间复杂度:比较次数与key的位置有关,查找第i个元素,需要n-i+1次,查找失败,需要n-1次

则ASL=(1+2+……+n)/n=(n+1)/2,时间复杂度为O(n)

空间复杂度O(1)

1.记录的查找概率不相等时如何提高查找效率?

   查找表存储记录原则——按查找概率高低存储。

   1)查找概率越高,比较次数越少;

   2)查找概率越低,比较次数较多。

2、记录的查找概率无法测定时如何提高查找效率?

方法——按查找概率动态调整记录顺序;

1)在每个记录中设一个访问频度域;

2)始终保持记录按非递增有序的次序排列;

3)每次查找后均将刚查到的记录直接移至表头。

优点:算法简单,逻辑次序无要求,且不同存储结构均适用。

缺点:ASL太长,时间效率低。

折半查找(二分或对分查找)

  每次将待查记录所在区间缩小一半。

设两个“指针”,low和high,另mid=(low+high)/2,若key<mid,则high=mid-1,若key>mid,则low=mid+1,通过这来缩小区间。若key==mid,则找到该值。

 非递归算法:

递归算法:

 查找效率:利用判定树进行分析,如对于下面序列数据

可以利用low和high建立一个判定树,以mid为根结点,则有<mid ,则为左孩子;>mid ,则为右孩子。

 则比较次数为:路径上的结点数,即结点的层数

设表长为n,则判定树深度h=\log_{2} n+1,此时树为深度为h的满二叉树,则表中每个结点的查找概率相同,为Pi=1/n。

则查找成功时:ASL=ΣPiCi=-1+\frac{n+1}{n}\log_{2}{n+1}  ≈ -1+\log_{2}{n+1},则折半查找的时间复杂度为O(logn)

折半查找优点:效率比顺序查找高。

折半查找缺点:只适用于有序表,且限于顺序存储结构。

分块查找

1.将表分成几块,且表或者有序,或者分块有序。如若i< j,则第j块中所有记录的关键字均大于第i块中的最大关键字。

2、建立"索引表”(每个结点含有最大关键字域和指向本块第一个结点的指针,且按关键字有序)。

查找过程:先确定待查记录所在块(顺序或折半查找),索引表再在块内查找(顺序查找)。

查找效率:ASL= Lb(对索引表查找的ASL)+Lw(对块内查找的ASL)

 分块查找优点:插入和删除比较容易,无需进行大量移动。

 分块查找缺点:要增加一个索引表的存储空间并对初始索引表进行排序运算。

适用情况:如果线性表既要快速查找又经常动态变化,则可采用分块查找。

树表的查找 

当表插入、删除操作频繁时,为维护表的有序性,需要移动表中很多记录。改用动态查找表——几种特殊的树。表结构在查找过程中动态生成。对于给定值key若表中存在,则成功返回,否则,插入关键字等于key,的记录。

二叉排序树 

二叉排序树(Binary Sort Tree)又称为二叉搜索树、二叉查找树。

二叉排序树或是空树,或是满足如下性质的二叉树:

(1)若其左子树非空,则左子树上所有结点的值均小于根结点的值。

(2)若其右子树非空,则右子树上所有结点的值均大于等于根结点的值。

(3)其左右子树本身又各是一棵二叉排序树。

如:

中序遍历后为:{3,12,24,37,45,53,61,78,90,99},为递增的。

  因此,中序遍历非空的二叉排序树所得到的数据元素序列是一个按关键字排列的递增有序序列。

如何在二叉排序树进行查找?

若查找的关键字等于根结点,成功。否则:

若小于根结点,查其左子树。若大于根结点,查其右子树。在左右子树上的操作类似。

利用如下二叉排序树储存结构:

 递归查找:

(1)若二叉排序树为空,则查找失败,返回空指针。

(2)若二叉排序树非空,将给定值key与根结点的关键字T-> data.key进行比较;

   ①若key等于T->data.key,则查找成功返回根结点地址;

   ②若key小于T->data.key,则进一步查找左子树;

   ③若key大于T->data.key,则进一步查找右子树。

 二叉排序树上查找某关键字等于给定值的结点过程,其实就是走了一条从根到该结点的路径。

比较的关键字次数=此结点所在层次数。

最多的比较次数=树的深度。

含有n个结点的二叉排序树的平均查找长度和树的形态有关 

最好情况:初始序列{45,24,53,12,37,93},ASL=log 2(n+ 1)-1;树的深度为: Llog 2n」+ 1;与折半查找中的判定树相同。(形态比较均衡) : O (log2n)。

最坏情况:初始序列{12,24,37 ,45,53,93},插入的n个元素从一开始就有序——变成单支树的形态!此时树的深度为n,ASL=(n+1)/2,查找效率与顺序查找情况相同: O(n)。 

如何在二叉排序树进行插入? 

若二叉排序树为空,则插入结点作为根结点插入到空树中。

否则,继续在其左、右子树上查找:树中已有,不再插入;树中没有,查找直至某个叶子结点的左子树或右子树为空为止,则插入,结点应为该叶子结点的左孩子或右孩子。插入的元素一定在叶子上

如何在生成二叉排序树? 

从空树出发,经过一系列的查找、插入操作之后,可生成一棵二叉排序树。

一个无序序列可通过构造二叉排序树而变成一个有序序列。构造树的过程就是对无序序列进行排序的过程。

插入的结点均为叶子结点,故无需移动其他结点。相当于在有序序列上插入记录而无需移动其他记录。关键字输入顺序不同,建立的二叉排序树不同

 如何在二叉排序树进行删除? 

从二叉排序树中删除一个结点, 不能把以该结点为根的子树都删除,只能删掉该结点,并且还应保证删除后所得的二叉树仍然满足二叉排序树的性质不变。

由于中序遍历二叉排序树可以得到一个递增有序的序列。那么,在二叉排序树中删去一个结点相当于删去有序序列中的一个结点。

将因删除结点而断开的二叉链表重新链接起来,防止重新链接后树的高度增加。

(1)被删除的是叶子结点,则直接删除该结点。

(2)被删除的结点只有左子树或者右子树,用其左子树或者右子树替换它。

(3)被删除的既有左子树也有右子树:

以其中序前趋值替换之(值替换),然后再删除该前趋结点。前趋是左子树中最大的结点。

也可以用其后继替换之,然后再删除该后继结点。后继是右子树中最小的结点。

 平衡二叉树

如何提高形态不均衡的二叉排序树的查找效率?

做“平衡化”处理,即尽量让二叉树形态均衡!——平衡二叉树

平衡二叉树(balanced binary tree):

  又称AVL树(Adelson-Velskii and Landis)。一棵平衡二叉树或者是空树, 或者是具有下列性质的二叉排序树:

①左子树与右子树的高度之差的绝对值小于等于1;

②左子树和右子树也是平衡二叉排序树。

  为了方便起见,给每个结点附加一个数字,给出该结点左子树与右子树的高度差。这个数字称为结点的平衡因子(BF)。平衡因子=结点左子树的高度-结点右子树的高度。根据平衡二叉树的定义,平衡二叉树上所有结点的平衡因子只能是-1,0,或1。

对于一棵有n个结点的AVL树,其高度保持在O(log2n)数量级,ASL也保持在O(log2n)量级。

当我们在平衡二叉树中插入一个结点时,可能造成失衡,即出现平衡因子绝对值大于1的结点。

如果在一棵AVL树插入一个结点导致失衡后,我们必须重新调整树的结构,使之恢复平衡。 

平衡失衡有四种类型:LL型,LR型,RL型,RR型。

调整原则:(1)降低高度。 (2)保持二叉排序树性质。 

 【1】LL型调整:

两种理解:(1)B结点带左子树α一起上升。A结点成为B的右孩子。原来B结点的右子树β作为A的左子树。 

                  (2)右旋:将B结点旋转上去作为根结点,A作为B的右孩子,B的右子树作为A的左子树。

【2】RR型调整

两种理解:(1)B结点带右子树β一起上升。A结点成为B的左孩子。原来B结点的左子树α作为A的右子树。 

                  (2)左旋:将B结点旋转上去作为根结点,A作为B的左孩子,B的左子树作为A的右子树。

【3】LR型调整 

两种理解:(1)C结点穿越A,B上升,B结点成为C的左孩子,A结点成为C的右孩子,原来C结点的左子树β作为B的右子树,原来C结点的右子树γ作为A的左子树。

                  (2)以B结点为基础先进行一次左旋,再以A为基础进行右旋。

【4】RL型调整

 两种理解:(1)C结点穿越A,B上升,B结点成为C的右孩子,A结点成为C的左孩子,原来C结点的左子树β作为A的右子树,原来C结点的右子树γ作为B的左子树。

                   (2)以B结点为基础先进行一次右旋,再以A为基础进行左旋。

散列表(哈希表)的查找

基本思想:记录的存储位置与关键字之间存在对应关系。对应关系:Hash函数:Loc(i)=H(keyi)。

散列方法(杂凑法): 选取某个函数,依该函数按关键字计算元素的存储位置,并按此存放;查找时,由同一个函数对给定值k计算地址,将k与地址单元中元素关键码进行比,确定查找是否成功。

散列函数(杂凑函数):散列方法中使用的转换函数。

散列表(杂凑表):按上述思想构成的表。 

冲突:不同关键码映射到同一个散列地址。Key1≠Key2,但H(Key1)=H(Key2)。

 同义词:具有相同函数值的关键字。

使用散列表需要解决两个问题:

【1】构造好散列函数:(a)所选函数尽可能简单,以便提高转换速度;(b)所选函数对关键码计算出的地址,应在散列地址集中致均匀分布,以减少空间浪费。

构造散列函数需要考虑的因素:①执行速度(计算散列函数需要的时间),②关键字长度,③散列表的大小,④关键字的分布情况,⑤查找频率。

【2】制定一个好的解决冲突的方案:查找时,如果从散列函数计算出的地址中查不到关键码,则应当依据解决冲突的规则,有规律地查询其它相关单元。

问题一,直接定址法

Hash(key)=a*key+b(a,b为常数)。

优点:以关键码key的某个线性函数值为散列地址,不会产生冲突。

缺点:要占用连续的地址空间,空间效率低。

问题一,除留余数法

Hash=key(mod p)(p是一个整数)

取p技巧:设表长为m,取p≤m且为素数。

问题二,开放地址法 

基本思想:有冲突时就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将数据元素存入。常用方法有:线性探测法,二次探测法,伪随机法。下面以Hash=key(mod p)为例:

【1】线性探测法

Hi=(Hash(key)+Di)mod m(i≥0&&i<m)

例:关键码集为{47,7,29,11,16,92,22,8, 3},表长取m,m为11,散列函数为Hash(key)=key mod 11;拟用线性探测法解决冲突。建散列表如下:

1).  47模11的余数为3,7模11的余数为7,放入表中:

2).  29模11的余数为7,与7冲突,需寻找下一个空的散列地址。由Hi=(Hash(29)+1)mod 11=8,8号位置为空,放入其中:

3).  11,16,92均没有冲突,放入表中:

4).  8模11为8,与29冲突,则由Hi=(Hash(8)+1)mod 11=9,找到9号位为空,放入其中;9模11为9,与8冲突,则由Hi=(Hash(8)+1)mod 11=10,找到10号位为空,放入其中;3模11为3,与47冲突,则由Hi=(Hash(8)+1)mod 11=4,Hi=(Hash(8)+2)mod 11=5,Hi=(Hash(8)+3)mod 11=6,找到6号位为空,放入其中。

  此时平均查找长度ASL=(1+2+1+1+1+4+1+2+2) /9=1.67。

【2】二次探测法

Hi=(Hash(key)+Di)mod m,m为散列表长度,且要求为4K+3的质数。Di的增量为1^2,(-1)^2,2^2,(-2)^2,……,q^2;

【3】伪随机探测法

Hi=(Hash(key)+Di)mod m,m为散列表长度,Di为伪随机数。

问题二,链地址法

基本思想:相同散列地址的记录链成一单链表

m个散列地址就设m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构。

 Step1:取数据元素的关键字key,计算其散列函数值(地址)。若该地址对应的链表为空,则将该元素插入此链表;否则执行Step2解决。

 Step2:根据选择的冲突处理方法,计算关键字key的下一个存储地址。若该地址对应的链表为不为空,则利用链表的前插法或后插法将该元素插入此链表。

优点:

(1)非同义词不会冲突,无聚集现象。

(2)动态申请空间,适合表长不清楚的情况。

散列表(哈希表)查找

使用平均查找长度ASL来衡量查找算法,ASL取决于:

(1)散列函数

(2)处理冲突的方法

(3)散列表的填装因子α 

α=表中填入的记录数➗哈希表长度 α越大,表示记录数越多,说明表填装的越满,发生冲突的可能性越大,查找时比较次数越多。

ASL与α有关,既不是严格的O(1),也不是严格的O(n)。

 散列表技术具有很好的平均性能,优于-些传统的技术。

链地址法优于开地址法。

除留余数法作散列函数优于其它类型函数。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值