向着更快更高 ——查找

学了线性表,操作过栈与队列,略过串、数组和广义表,建过树,搜过图,现在到了查找——对于大数据的查找优化。

这一节,是以往知识的总结提高。

一、首先是最简单的查找——顺序查找  

  要求:顺序存储结构。

  方法思路:对于给定值(如 key ),在已有的存储中依次比对关键字。

  结果:(1)查找成功,返回所需要的信息(如下标);

     (2)查找失败,此时已遍历整个存储结构。返回约定的失败标识符(如-1)

  最坏情况下的时间复杂度:O(n)。

二、略高级的查找——折半查找

  要求:顺序存储结构,内容按关键字有序排列。

  方法思路:有三个值:left(搜索范围的左边界),right(搜索范围的右边界),mid(搜索范围的中间位置)。

       很明显,当 left <= right 时,还有至少一个元素待比较;当 left > right ,待比较元素为0.(循环结束的判断条件)

       每次取 mid 位置下关键字(用 key[mid] 表示)与给定值比较,会有三种结果。

       (1)key[mid] == 给定值:查找成功。

       (2)key[mid] < 给定值:right 取 mid-1 。(mid的右边都大于给定值,如果给定值在存储中存在,一定在mid的左边)

       (3)key[mid] > 给定值:left 取 mid+1 。(mid的左边都小于给定值,如果给定值在存储中存在,一定在mid的右边)

  结果:(1)查找成功,返回所需要的信息(如下标);

     (2)查找失败,每次均比较剩下元素的中间位置的元素,所以无需遍历整个存储结果。返回约定的失败标识符(如-1)

  最坏情况下的时间复杂度:O(log2n)(是以2为底,n的对数)。

              在n个元素的情况下,每次折半后剩下的元素为:n,n/2,n/4,....n/2^k,其中k就是循环的次数。n/2^k取整后>=1,即令n/2^k=1,可得k=log2n。

三、与以上两种截然不同的查找——散列查找法

  要求:一个有限连续的地址空间作为散列表,一个散列函数表示关键字与存储位置的关系,一种良好的解决冲突问题的方法。

  方法思路:输入关键字,根据散列函数计算的到位置信息(通常是下标),然后比较这个位置下的 key 与关键字是否相等。

       通常会有以下几种结果:

       (1)相等,查找成功。

       (2)不相等,也就意味着这里在插入时发生了冲突,要根据所选择的的解决冲突的方法不同而重新计算位置,然后再次比较。

       (3)在重新计算位置时,如果重新算出的位置已经超出散列表的最大表长,那么就表示查找失败。

  结果:(1)查找成功,返回所需要的信息(如下标);

     (2)查找失败,仅仅遍历的几个在建立散列表是发生过冲突的关键字,无需遍历其余并没有发生冲突的元素。返回约定的失败标识符(如-1)

  时间复杂度:与待查找个数 n 无关,通常查找成功的时间复杂度为 O(1)。

  

  散列表解决冲突的方法:开放地址法和链地址法。

    开放地址法(主要是以下两种):

      线性探测法:将散列表当成一个循环表,从当前一直往下找到一个空位置,插入元素;

      二次探测法:初始位置 +1, -2, +3.·······意即,找到初始位置的下一个(H0+1),看是否为空;若不为空,则找到初始位置的下一个的下一个(H0+1-2)·······

            实际算法(散列函数为 除留余数法):Hi = (关键字 + di)% p,i=1,2····k(k<=m-1)

                              di = 1^2, -1^2, 2^2,-2^2·······,+k^2, -k^2。(k<=m/2)

    链地址法:将每一个位置都是一个链表,如果发生冲突,就再次申请一个个空间,接在后面。

四、最后说说我的感想吧,对于这一节内容,感触最深的就是,第一次吧,在PTA上,把时间降到个位数。这是我以前不敢想象的,果然学无止境回头是岸,但是一旦回头又怎能了解到远方的宽广?

  (我欢迎指出其中有错的,毕竟这一节内容我还有很多没有深入了解)

 

posted @ 2019-06-02 23:53  一游此处  阅读(139)  评论(0编辑  收藏  举报