查找的知识点总结

来源:互联网 发布:加州101电台知乎 编辑:程序博客网 时间:2024/06/08 18:03

        说到排序必然就要说到查找,所谓查找(或搜索)指的是从数据文件中找出满足某些条件的记录,用来查找的条件称为“键值(key)”,就如同排序所用的键值一样。一次查找所用的时间很短,但当程序中需要大量使用查找操作的时候,查找的时间消耗效率就显得非常重要。

        同排序一样,可根据数据量的大小将查找分为内部查找和外部查找。从另一个角度看,又可分为静态查找动态查找,定义如下:

           1、静态查找:查找的表格或文件的内容不会被改动,例如符号表的查找就是一种静态查找。

           2、动态查找:查找的表格或文件的内容可能会被改动,例如在树状结构中所谈的B树查找就属于一种动态查找。

        常见的查找方法有顺序查找、二分查找、斐波那契查找、插值法、哈希法、m路查找树、B树等。现一一介绍:

    一、顺序查找

        通常用于未经排序的文件,又称为线性查找。查找的过程从文件第一项数据开始,按序比较每一个键值。因此顺序查找的平均比较次数为(n+1)/2。

        当n很大时,顺序查找并不适合,它是一种适用于小数据文件的查找方法。

        其优点仅仅是文件或数据事前不需要经过任何的处理与排序,其缺点是速度慢。

    二、二分查找

        若要查找的数据已经事先排序好,则可以使用更快的查找方法,如二分查找。二分查找法是将数据分割成两个部分并比较待查找的键值与中间值的大小,若相等,则找到,否则进入可能存在的部分,再分割,如此往复,直到找到或确定不存在。

        使用二分查找要求被查找的数据事先已排序,且数据量不能太大,必须能在内存中执行(所以是内部查找)。此法适用于不需要增删的静态数据。因为每次查找都会比上一次少一半,所以最多只需要比较log(n) + 1或log(n+1)次,时间复杂度为O(logN)。

    三、插值查找

        又叫做插补查找法,是二分查找法的改进版。它是按照数据位置的分布,利用公式预测数据所在的位置,再以(非平均二分)二分法去渐渐逼近。使用插值法是假设数据平均分布再数组中,而每一项数据的差距相当接近或有一定的距离比例。插值法的公式为:

        Mid = low + ((key - data[low]) / (data[high] - data[low])) * (high - low)

       其中key是要查找的键值,data[high]、data[low]是剩余待查找记录中的最大值和最小值。

        步骤如下:

           1、将记录从小到大用1 - n编号,并令low = 1, high = n;

           2、当low < high时,重复步骤3 - 6

           3、令mid = low + ((key - data[low]) / (data[high] - data[low])) * (high - low)

           4、若key < data[mid],则令high = mid - 1

           5、若key = data[mid],表示成功查找到键值

           6、若key > data[mid],则令high = mid + 1

        对于插值查找,如果数据的分布越平均,则查找速度越快。此法的时间复杂度取决于数据分布情况,平均而言优于O(logN)

     四、斐波那契查找

        斐波那契查找和插值查找一样,都可以说是二分查找的改进版,也都是以分割范围来进行查找,不同的是斐波那契查找和插值查找都不是用对半分割。斐波那契查找以斐波那契级数的方式来分割。

        斐波那契查找的好处是只用到加减运算而不需要用到乘除运算,这从计算机运算的过程来看,效率会高于前两种查找方法。斐波那契查找需要用到斐波那契查找树,其建立原则如下:

        也就是说当数据个数为n时,找到一个最小的斐波那契数Fib(k + 1) > n + 1,则Fib(k)就是这棵斐波那契树的树根,而Fib(k - 2)则是与左右子树开始的差值,左子树用减的,右子树用加的。斐波那契树的存储使用数组,查找思路如下:

        1)比较数组下标Fib(k)和键值key;

        2)当key < Fib(k)时,表示所查找的key落在1到Fib(k) - 1之间,继续查找1到Fib(k) - 1之间的数据;

        3)若key = Fib(k),成功查找到数据;

        4)当key > Fib(k)时,表示所查找的key落在Fib(k) + 1到Fib(k) + 1之间,继续查找Fib(k) + 1到Fib(k) + 1之间的数据;

        平均而言,斐波那契查找的比较次数少于二分查找,但在最坏情况下二分查找较快(O(logN))。另外,斐波那契查找算法较为复杂,需要额外产生斐波那契树。

        备注:二分查找是进行加法与除法运算的(mid = (low + high) / 2),插值查找则进行更复杂的四则运算(mid = low + (high - low) * ((key - a[low]) / (a[high] - a[low]))),而斐波那契查找只进行最简单的加减法运算(mid = low + F[k-1] - 1),在海量数据的查找过程中,这种细微的差别可能会影响最终的效率。

        五、哈希查找

        哈希法(散列法)通常和查找联系在一起,主要原因是哈希法不仅被用于数据的查找,在数据结构的领域中,还能将它应用在数据的建立、插入、删除和更新里。

        例如符号表在计算机中的应用很广泛,包含汇编程序、编译程序、数据库使用的数据字典等,都是利用提供的名称来找到对应的属性。符号表按特性可分为静态表和动态表。而哈希表则属于静态表的一种,常常被用于处理数据的加密和压缩。

        简单来说,哈希法(hashing)就是将本身的键值,通过特定的数学函数运算转换成相对应的数据存储地址。所使用的数学运算就称为“哈希函数“,先介绍有关的名词:

            bucket(桶):哈希表中存储数据的位置,每一个桶对应到唯一的一个地址(bucket address)。桶就好比一个记录。

            slot(槽):每一个记录中可能包含好几个字段,而slot指的就是”桶“中的字段。

            collision(碰撞):若两项不同的数据(键值),经过哈希函数运算后,对应到相同的地址,就称为碰撞。

            溢出:如果数据经过哈希函数运算后,所对应的bucket(桶)已满,则会使bucket发生溢出。

            哈希表(哈希空间):存储记录的连续内存。哈希表是一种类似于数据表的索引表格,其中可分为n个bucket,每个bucket又可分为m个slot,即n行,m列。

            同义词:当两个标识符I1和I2,经哈希函数f(x)运算后所得的数值相同,即f(I1) = f(I2),则成I1和I2对于f(x)这个哈希函数是同义词。

            加载密度:标识符的使用数目除以槽的总数——a(加载密度) = n(标识符的使用数目) / (s(每一个桶内的槽数) * b(桶的数目)),如果a值越大,则表示哈希空间的利用率越高,碰撞或溢出的概率也就越高。

            完美哈希:指没有碰撞也没有溢出的哈希函数,一般而言只有在静态表中才会使用(设计完美哈希)。

        通常,设计哈希函数应当减少碰撞和溢出的发生,且不宜过于复杂,越容易计算越好。同时,应尽量把文字的键值转换成数字的键值,以利于哈希函数的运算。所设计的哈希函数计算得到的值,应尽量能均匀地分布在每一个桶中。

        常见的哈希函数

        1)除留余数法:最简单的哈希函数,将数据除以某一个常数(最好为质数)后,取余数来当索引。

        2)平方取中法:先计算数据的平方,然后取中间的某段数字作为索引。

        3)折叠法:将数据转换成一串数字后,拆成几个部分,最后再把这几个部分加起来作为这个键值的索引(桶地址)。

        4)相乘取整法:首先用关键字key乘上某个常数A(0<A<1),并抽取出key*A的小数部分,然后用m乘以该小数后取整

        5)随机数法:选择一个随机函数,取关键字的随机函数值为它的散列地址

        碰撞与溢出的处理

        没有一种哈希函数能够确保不会发生碰撞与溢出,尤其是在数据量大的时候特别容易发生。常见的处理方法有:

        1)线性探测法:若发生碰撞,则以线性的方式往后寻找空的存储位置,一旦找到就把数据放进去。线性探测法通常把哈希的位置视为环形结构以提高利用率。

        2)平方探测:线性探测法会使键值经常聚集在一起,当发生溢出时,下一次的查找地址为(f(x) + I ^ 2) mod B(常数) 与 (f(x) - I ^ 2) mod B

        3)再哈希:设置一系列的哈希函数,依次尝试使用,直到不会发生溢出为止。

        4)链表:将哈希表的所有空间建立n个链表,最初只有n个链表头。如果发生溢出就把相同地址的键值连接到链表头的后面,形成一个键表,直到所有的可用空间全部用完为止。

        哈希查找比其他查找的速度都要快,理想情况下时间复杂度仅为O(1)。它不仅可以用来查找,还可以很方便地使用哈希函数进行创建、插入、删除与更新。重要的是,通过哈希函数创建哈希表来进行查找文件,事先不需要排序,但需要经过一个哈希的过程,这也是它和一般的查找法较大的差异之处。

0 0
原创粉丝点击