数据结构与算法学习之查找技术

查找是对数据、文件处理时常使用的一种操作。查找算法主要分为两类,静态查找和动态查找。

静态查找是指查找过程中标的结构始终不会发生变化,而动态查找表会发生变化,通常伴随着插入和删除操作。一般衡量查找算法的优越性主要看平均查找长度和最大查找长度。

一、静态查找表

1.顺序查找

算法思想:从表的一端开始顺序扫描线性表,依次将扫描到的结点关键字与给定值K比较,若当前扫描到的结点关键字与k相等则查找成功;若扫描结束后,仍未找到关键字等于K的结点,则查找失败。

复杂度:若表中一共有N个记录,则顺序查找的最大查找长度为N,平均查找长度为(N+1)/2,算法的时间复杂度为O(N)。

应用:它可以用于顺序存储结构,也可以用于链表结构。

2.折半查找(二分查找)

算法思想:对一有序表中的元素,从初始的查找区间开始,每经过一次与当前查找区间的中点位置(通常(n+1)/2向下取整)上的结点关键字进行比较,若相等,则查找成功,否则,当前查找区间的缩小一半,按k值大小在某半个区间内重复相同的步骤进行查找,直到查找成功或失败为止。

复杂度:若表中一共有N个记录,则顺序查找的最大查找长度为【log2N】+1,平均查找长度为log【N+1】-1,算法的时间复杂度为O(log【N】)。

应用:但是折半查找是建立在已经排序的表的基础上的,而且折半查找只适用于顺序结构,不适用于链表结构,因为链表结构直接访问表的中间节点,不能像顺序结构一样通过下表操作访问,链表要通过头指针移动找到中间节点。

3.分块查找(索引顺序查找)

算法思想:将原表分成若干块,各块内部不一定有序,但表中的块是"分块有序"的,并抽取各块中的最大关键字及其起始位置建立索引表。因为索引表是有序的,分块查找就是先用二分查找或顺序查找确定待查结点在哪一块,然后在已确定的块中进行顺序查找(不能用二分查找,因为块内是无序的)。

复杂度:分块查找实际上是两次查找过程,其平均查找长度为两次平局查找长度之和,它的算法效率介于顺序查找和二分查找之间。

应用:此种方法经常用于外部查找,将索引表放在内存中,原始数据的分块有序表以子表的形式存在外存储器上,先用索引表确定外存的子表,在将子表调入内存中查找。

二、动态查找

1.二叉排序树(BST)又称二叉查找树
算法思想:主要是根据二叉排序树的性质,1)若它的左子树非空,则左子树上所有结点的值均小于根结点的值;2)若它的右子树非空,则右子树上所有结点的值均大于根结点的值;3)左、右子树本身又是一棵二叉排序树。之后其查找方法类似于折半查找。

复杂度:它的关键字比较次数不超过树的深度,二叉排序树的查找复杂度与树形有关,在生成的过程中比较匀称,平衡的二叉树时约为lgn.在最坏的情况下,极不平衡时,退化为一个链表,此时的二叉排序树的高度将达到O(n),复杂度O(n),为这种情况应当避免。为了构造形状比较均匀的二叉树,引出平衡二叉树的概念。
应用:其实二叉排序树和折半查找的复杂度差不多,但是折半查找只是用于顺序结构,对这种表的插入和删除操作很麻烦,而二叉排序树可以适用于链表存储结构,插入和删除操作灵活方便。

2.B-

M阶B-树的性质1)树的根或者是一个叶子,或者含有[2,M]个孩子;2)除树根外,其他非叶子节点的儿子数在[【2/M】,M]之间。

3)所有的树叶都在相同的深度上。4)所有的数据都存储在树叶上。

算法思想:与二叉查找树类似。

应用:适用于大量文件的查找,它适合在磁盘等直接存取设备上组织动态的查找表,是一种外查找算法。

 

上面的查找方法都是建立在比较关键字的基础上,下面这种方法不需要比较关键字,称为哈希查找或者散列查找。

以下是在一片博客上看到的,总结的不错,本文来自CSDN博客http://blog.csdn.net/mosoft/archive/2005/06/06/388410.aspx

散列技术:可以无需任何比较就找到待查关键字,其查找的期望时间为O(1).
散列表的概念:就是将所有可能出现的关键字的集合U(全集)映射到一个表T[0..m-1]的下标集上,这个表就是散列表。
而关键字与这个表地址之间以什么样的关系发生联系呢,这就要通过一个函数来建立,这个函数是以U中的关键字为自变量,以相应结点的存储地址为函数值,它就称为散列函数或者哈希函数。将结点按其关键字的散列地址存储到散列表的过程称为散列。根据某种散列函数,一个关键字的散列函数值是唯一的,但是有可能两个或多个不同关键字的函数值是相同的,这时就会把几个结点存储到同一个表位置上,这时就造成冲突(或碰撞)现象,这两个关键字称为该散列函数的同义词。要完全(不是"安全")避免冲突需满足两个条件,一是关键字集合U不大于散列表长m,另一个是选择合适的散列函数。通常情况下U总是大大于m的,因此不可能完全避免冲突。冲突的频繁程度还与表的填满程度相关。装填因子α表示表中填入的结点数n与表长m的比值,即n/m,通常取α≤1,因为α越大,表越满,冲突的机会也越大。散列函数的选择有两条标准:简单和均匀。看看h(ki)=0这样的函数,简单是简单,但绝不均匀。

下面是常见的几种散列函数构造方法:
(1)平方取中法
(2)除余法:它是用表长m来除关键字,取余数作为散列地址。若选除数m是关键字的基数的幂次,就会使得高位不同而低位相同的关键字互为同义词。因此最好选取素数为除数.
(3)相乘取整法:有两个步骤,先用关键字key乘上某个常数A(0)
(4)随机数法,此法以关键字为自变量,通过一随机函数得到的值作为散列地址。

处理冲突的方法:当不可避免发生冲突时,就必须对冲突加以解决,使发生冲突的同义词能存储到表中。通常有两类方法处理冲突:开放定址法和拉链法。前者是将所有结点均存放在散列T[0..m-1]中,后者是将互为同义词的结点链成一个单链表,而将此链表的头指针放在散列表中。

开放定址法的一般形式为:hi=(h(key)+di)%m 1≤i≤m-1。开放定址法要求散列表的装填因子α≤1。开放定址法又有线性探查法、二次探查法和双重散列法之分。
(1)由于线性探查法在构造散列表时,遇到冲突(有同义词)的时候会按探查序列向后面的空地址插入,从而使原来应插入到此位置的结点又与它发生冲突,当一连串的位置均已有结点时,本应插入到这些位置的结点又只能将其插入到更后面的同一个空结点上,这种散列地址不同的结点争夺同一个后继散列地址的现象就是聚集或堆积。(注意,同义词发生冲突不是堆积)
为了减小堆积现象的发生,可以用二次探查法和双重散列法进行探查。
(2)拉链法解决冲突的做法是,将所有关键字为同义词的结点链接在同一个单链表中。

与开放定址法相比,拉链法有如下几个优点:
(1)拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;(简单无堆积)
(2)由于拉链法中各链表上的结点空间是动态申请的,故它更适于造表前无法确定表长的情况;(动态申表长)
(3)开放定址法为减少冲突要求装填因子α较小,当结点规模较大时会浪费很多空间,拉链法中α可以大于1,且结点较大时,其指针域可忽略不计,因此节省空间;(空间可节省)
(4)拉链法构造的散列表删除结点易实现,而开放定址法中则不能真正删除结点只能做删除标记。(删除易实现)
拉链法也有缺点:当结点规模较小时,用拉链法中的指针域也要占用额外空间,还是开放定址法省空间。

在散列表上的运算有查找、插入和删除,主要是查找。这三个操作的算法并不复杂,也容易理解。散列表的平均查找长度不是结点个数n的函数,而是装填因子α的函数。α越小,冲突的概率越小,但空间的浪费将增加,当α大小合适时,散列表上的平均查找长度就是一个常数,时间性能是O(1).

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值