查找算法(一)顺序表查找

参考:
博客 https://blog.csdn.net/sayhello_world/article/details/77200009
《大话数据结构》

顺序表查找分为:

  • 顺序查找

  • 有序查找

    • 折半查找
    • 插值查找
    • 斐波那契查找
  • 线性索引查找
    • 稠密索引查找
    • 分块索引查找
    • 倒序查找

1.顺序查找

基本思想:顺序查找是最基本的查找技术,查找过程是:从表中的第一个或者最后一个元素开始,依次将元素和待查找的值进行比较,若相同,则查找成功;若直到表尾或表头,元素和待查找的值都不相同,则查找不成功

代码实现:

public class SequentialSearch {

    public static void main(String[] args) {
        int[] a = {0,1,16,24,35,47,59,62,73,88,99};
        System.out.println(sequential(a,62));
    }

    public static int sequential(int[] a, int target){
        int len = a.length;

        for(int i=0;i<len;i++){
            if(a[i]==target){
                return i;
            }
        }
        return -1;
    }
}

复杂度分析:
查找成功的平均查找时间为(1+2+3+…+n)/n = (n+1)/2;
查找失败需要进行n+1次比较;
所以顺序查找的时间复杂度为O(n)

2.有序查找

2.1折半查找

折半查找又叫二分查找,其前提是序列为有序的,若为无需序列,需要先进行排序。

基本思想: 因为序列为有序序列,首先将待查找的数据k与序列中间元素a[mid]进行比较,如相同,则查找成功;若k > a[mid],则表明其存在于后半部分,否则在前半部分,由此继续递归查找,直到查找到或者查找失败。

代码实现:

import java.util.Arrays;

public class BinarySearch {

    public static void main(String[] args) {
        int[] a = {0,1,16,24,35,47,59,62,73,88,99};
        System.out.println(binary(a,62));

    }

    public static int binary(int[] a, int target){
        int len = a.length;
        int i = 0;
        int j = len-1;
        int mid;

        //首先对数据进行排序
        Arrays.sort(a);

        while(i<=j){
            mid = (i+j)/2;
            if(a[mid] == target)
                return mid;
            if(target > a[mid]){
                i = mid+1;
            }else{
                j = mid - 1;
            }
        }
        return -1;  
    }
}

时间复杂度分析: 每次比较可以排除掉一半的数据,所以时间复杂度为O(logn)。

注意:二分查找的前提是元素是有序的,所以对于静态表,一次排序后不再变化,其查找效率是不错的,但是对于需要频繁插入和删除的数据来说,单纯维护其序列有序就会带来不小的开销,所以在这种情况下,二分查找并不适用。

2.2 插值查找

插值查找是根据待查找的数据key和序列中最大最小值比较后的查找方法,是二分查找的一种变形。

基本思想: 对于折半查找来说,每次都从1/2处进行,但是例如对于1~10000中间的100数里查找数值5来说,首先需要考虑从数组下标较小的开始查找,而不是直接从1/2处,这样才可以提供查找的效率。
所以将查找点的计算改为:(key - a[low]) / (a[high] - a[low]);
所以 mid = low + (key - a[low]) * (high - low) / (a[high] - a[low])

代码实现:

只需要将二分查找中的mid = (i+j)/2 改为
mid = i + (j-i)*(target - a[i])/(a[j]-a[i]);

时间复杂度分析: 其平均时间复杂度O(logn),对于表较长,且数值均匀分布的序列来说,插值查找平均性能要比折半查找好得多,但是对于极端不均匀分布的数据,差值查找不一定是是合适的选择。

2.3斐波那契查找

斐波那契查找也是二分查找的一种变形,也是一种有序查找算法。
首先介绍下黄金分割:是指饰物各部分之间存在的一定数学比例关系,即将整体一分为二,较大部分/较小部分 = 整体 / 较大部分,其比值约为1.618:1。
对有斐波那契数列,后面每一个数是前面两个数字的和,并且可以发现,随着斐波那契数列的递增,前后两个数字的比值越来约接近0.618,在斐波那契查找中,便是利用到这一特性。

基本思想: 将黄金分割的概念运用在查找点的计算上,提高查找效率。
如下图所示(图片来源https://blog.csdn.net/sayhello_world/article/details/77200009,侵权删),序列的长度为斐波那契数列中的一个数值减1,即n = F(k) - 1; 那么插入点mid = low + (F(k-1) - 1)。
若 key == a[mid], 查找成功;
若 key > a[mid], 则在右半部分,所以有left = mid+1,且 k = k-2;
若 key < a[mid], 则在左半部分,所以有right = mid-1,且 k = k-1;
这里写图片描述

代码实现:

import java.util.Arrays;

public class FibonacciSearch {
    public static void main(String[] args) {
        int[] a = {0,1,16,24,35,47,59,62,73,88,99};
        System.out.println(Fibonacci(a,62));

    }

    public static int Fibonacci(int[] a, int target){
        int len = a.length;
        int[] F = creatFibonacci();

        int k = 0;
        //遍历斐波那契数列,找到k的值
        for(int i = 0;i<F.length;i++){
            if(len > F[k]-1){
                k++;
            }
        }

        //当F[k]-1>n的是否,需要不全待查找的序列,Arrays类中的copyOf方法将a数组补长到F[k]-1
        int[] temp = Arrays.copyOf(a, F[k]-1);
        for(int i = len;i<F[k]-1;i++){
            temp[i] = temp[len-1];
        }

        int left = 0;
        int right = F[k]-1;
        int mid;

        while(left<right){
            mid = F[k-1] - 1;
            if(temp[mid] == target)
                return mid;
            if(target > temp[mid]){
                left = mid+1;
                k -= 2;
            }else{
                right = mid - 1;
                k -= 1;
            }
        }

        return -1;
    }

    //初始化一个斐波那契数组,长度为20,F[19] = 6765,已经挺大的了,所以这里定义的长度20
    public static int[] creatFibonacci(){
        int[] F = new int[20];

        F[0] = 0;
        F[1] = 1;

        for(int i = 2;i<20;i++){
            F[i] = F[i-1] + F[i-2];
        }
        return F;
    }
}

复杂度分析: 时间复杂度为O(logn)

总结:顺序查找不需要序列为有序的;而折半查找、插值查找以及斐波那契查找则均是在序列有序的基础上进行的,只是查找点上有不同,各有各的优势,在实际使用当中,需要综合考虑来做出选择。

3.线性索引查找

在现实中,数据是不断处于动态增长且无序的,前面有序查找算法需要基于有序的基础上,所以对于大量增长非常快的数据集来说,维护数据有序的代价是非常非常大的。所以出现了索引。
索引就是把一个关键字与它对应的记录相关联的过程,加快查找速度。
线性索引查找就是将索引项集合组织为线性结构,称为索引表。

3.1稠密索引

稠密索引是指在现象索引中,将数据集中的每个记录对应一个索引项。
这里写图片描述
索引项按照关键码有序排列,当查找关键字时,可以通过折半、插值、斐波那契等有序算法进行查找,大大提高效率。
对于稠密所以,意味着索引和数据集具有同样的长度,当数据非常大时,稠密索引的查找效率大大降低。

3.2分块索引

为减少索引个数,将数据集进行分块,对每块建立索引项。
这些块需要满足一下两个条件:

  • 快内无序,即对每一块内的记录不要求有序,维护快内有序需要付出大量的时间和空间代价;
  • 块间有序,快间有序,才能在查找中带来效率。

这里写图片描述

分块索引查找步骤:
(1)在分块索引表中查找关键字所在块,因为块间有序,所以同样可以使用有序查找算法提高效率;
(2)根据块首指针找到相应的块,因为块中无序,所以在块中顺序查找的得到关键码。

分块查找的时间复杂度为:O(√n)

3.3倒序索引

倒序索引的典型应用就是搜索引擎。
倒序索引是建立被搜索的关键词和含这些关键字的页面之间的映射,其中倒序指的是从关键词中查找到对应的源,而不是从源中查找到相应的关键词。
将倒序索引表排序后,同样可以通过有序查找算法快速查找到关键字,进而查找到包含关键字的页。
例如:为了检索关键词java,首先从倒序索引表中,找到该关键词,然后便可以查找到java所在的页了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值