总结四种常见的查找算法

1、顺序查找

1.1、定义

顺序查找又名线性查找,是一种最简单的查找方法,它的基本思想是从第一个记录开始,逐个比较记录的关键字,直到和给定的K值相等,则查找成功;若比较结果与文件中n个记录的关键字都不等,则查找失败。

1.2、代码体现

public class SequenceSearch {

    public static void main(String[] args) {
        int[] arr = {3, 2, 99, 0, -1, 32};
        int target = -1;
        int index = lookup(arr, target);
        if (index == -1) {
            System.out.println("没有找到目标值!");
        } else {
            System.out.printf("数据%d的下标为%d", target, index);
        }
    }

    /**
     * 目前找到一个目标值就进行返回,如果有多个值需要找的话,就需要将其下标存入到集合或数组中
     *
     * @param arr 目标数组
     * @param i   要查找的目标值
     * @return 如果找到就返回该目标值的下标,没找到就返回-1
     */
    private static int lookup(int[] arr, int i) {
        for (int j = 0; j < arr.length; j++) {
            if (arr[j] == i) {
                return j;
            }
        }
        return -1;
    }
}

2、二分查找

2.1、定义

二分查找也称折半查找,它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。

2.2、思路分析

一、第一步先确定数组的中间下标:mid = (left + right)/ 2,其中left为给定数组的最右边下标0,right为给定数组的长度-1;
二、将要查找的目标值targetValue和arr[mid]相比较:
如果targetValue>arr[mid],说明要查找的数在mid的右边,需要向右递归查找;
如果targetValue<arr[mid],说明要查找的数在mid的左边,需要向左递归查找;
如果targetValue==arr[mid],则说明该值已找到,返回其下标即可
三、递归结束的条件:
找到目标值就结束递归,或是递归完整个数组后,仍然没有找到targetValue,此时也需要结束递归,即当left>right时,就需要退出。

2.3、代码体现

public class BinarySearch {

    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        int index = binarySearch_1(arr, 0, arr.length - 1, 2);
        System.out.println("index == " + index);
        int[] arr2 = {1, 2, 3, 4, 4, 4, 5, 6, 7, 8, 9};
        List<Integer> indexList = binarySearch_2(arr2, 0, arr.length - 1, 1);
        System.out.println("index == " + indexList);
    }

    /**
     * 该方法只能找到目标数组中一个符合目标值的结果
     *
     * @param arr         目标数组
     * @param left        左索引
     * @param right       右索引
     * @param targetValue 目标值
     * @return 找到就返回目标值下标,没有找到就返回-1
     */
    private static int binarySearch_1(int[] arr, int left, int right, int targetValue) {
        //当left>right时,说明递归整个数组都没有找到要找的值,同时,如果要查的值大于最大的,小于最小的,也直接返回-1
        if (left > right || targetValue > arr[arr.length - 1] || targetValue < arr[0]) {
            return -1;
        }
        int mid = (left + right) / 2;
        int midValue = arr[mid];
        if (targetValue > midValue) { //向右递归
            return binarySearch_1(arr, mid + 1, right, targetValue);
        } else if (targetValue < midValue) { //向左递归
            return binarySearch_1(arr, left, mid - 1, targetValue);
        } else {
            return mid;
        }
    }

    /**
     * 当数组中有多个要找的相同数值时,将所有满足的数值都查找到
     *
     * @param arr         目标数组
     * @param left        左索引
     * @param right       右索引
     * @param targetValue 目标值
     * @return 返回装有所有满足目标值下标的list
     */
    private static List<Integer> binarySearch_2(int[] arr, int left, int right, int targetValue) {
        //当left>right时,说明递归整个数组都没有找到要找的值
        if (left > right || targetValue > arr[arr.length - 1] || targetValue < arr[0]) {
            return new ArrayList<>();
        }
        int mid = (left + right) / 2;
        int midValue = arr[mid];
        if (targetValue > midValue) { //向右递归
            return binarySearch_2(arr, mid + 1, right, targetValue);
        } else if (targetValue < midValue) { //向左递归
            return binarySearch_2(arr, left, mid - 1, targetValue);
        } else {
            List<Integer> indexList = new ArrayList<>();
            //向mid索引值的左边扫描,将所有满足的元素下标都加入到indexList集合中
            int temp = mid - 1;
            while (true) {
                if (temp < 0 || arr[temp] != targetValue) {
                    break;//退出
                }
                //否则就将temp放入到indexList中
                indexList.add(temp);
                temp -= 1;//将temp左移
            }
            indexList.add(mid);
            //向mid索引值的左边扫描,将所有满足的元素下标都加入到indexList集合中
            temp = mid + 1;
            while (true) {
                if (temp > arr.length - 1 || arr[temp] != targetValue) {
                    break;//退出
                }
                //否则就将temp放入到indexList中
                indexList.add(temp);
                temp += 1;//将temp左移
            }
            return indexList;
        }
    }
}

3、插值查找

3.1、定义

插值查找,有序表的一种查找方式。插值查找是根据查找关键字与查找表中最大最小记录关键字比较后的查找方法。插值查找基于二分查找,将查找点的选择改进为自适应选择,提高查找效率。
即将二分查找中的公式改为:
mid=left+(right-left)*(targetValue-arr[left])/(arr[right]-arr[left])

3.2、代码体现

public class InterpolationSearch {

    public static void main(String[] args) {
        int[] arr = {1, 5, 6, 8, 9, 11};
        int index = interpolationSearch(arr, 0, arr.length - 1, 5);
        System.out.println("index == " + index);
    }

    /**
     * @param arr         目标数组
     * @param left        左索引
     * @param right       右索引
     * @param targetValue 目标值
     * @return 找到就返回目标值下标,没有找到就返回-1
     */
    private static int interpolationSearch(int[] arr, int left, int right, int targetValue) {
        //当left>right时,说明递归整个数组都没有找到要找的值,同时,如果要查的值大于最大的,小于最小的,也直接返回-1
        if (left > right || targetValue > arr[arr.length - 1] || targetValue < arr[0]) {
            return -1;
        }
        //与二分查找相比,插值查找的主要变化就在于求中间值的算法变化
        int mid = left + (right - left) * (targetValue - arr[left]) / (arr[right] - arr[left]);
        int midValue = arr[mid];
        if (targetValue > midValue) { //向右递归
            return interpolationSearch(arr, mid + 1, right, targetValue);
        } else if (targetValue < midValue) { //向左递归
            return interpolationSearch(arr, left, mid - 1, targetValue);
        } else {
            return mid;
        }
    }
}

4、斐波那契查找

4.1、定义

斐波那契搜索就是在二分查找的基础上根据斐波那契数列进行分割的。在斐波那契数列找一个等于略大于查找表中元素个数的数 F[n], 将原查找表扩展为长度为F【n】(如果要补充元素,则补充重复最后一个元素,直到满足F[n]个元素),完成后进行斐波那契分割,即F[n]个元素分割为前半部分F[n-1]个元素,后半部分F[n-2]个元素,找出要查找的元素在哪一部分并递归,直到找到。

4.2、原理

斐波那契查找与二分查找和插值查找相似,仅仅改变了中间节点mid的位置,mid不再是中间或插值得到,而是位于黄金分割点附近,即mid=low+F(k-1)-1
(F代表斐波那契数列),入下图所示:
在这里插入图片描述
对F(k-1)-1的理解:
1、由菲波那切数列F[k]=F[k-1]+F[k-2]的性质可知:
(F[k]-1)=(F[k-1]-1)+(F[k-2]-1)+1。也就说明了,只要顺序表的长度为F[k]-1,则可以将该表分成长度为F[k-1]-1和F[k-2]-1的两段,从而中间位置为
mid=low+F(k-1)-1;
2、类似的,每一个子段也可以用相同的方式分割;
3、需要注意一点:顺序表的长度n不一定刚好等于F[k]-1,所以需要将原来的顺序表长度n增加至F[k]-1。这里的k值只要能使得F[k]-1恰好大于或等于顺序表长度n即可。由以下代码得到,顺序表长度增加后,新增的位置(从n+1到F[k]-1位置),都赋为n位置的值即可。while(n>F[k]-1) k++;

4.3、代码体现

public class FibonacciSearch {

    public static int maxSize = 20;

    public static void main(String[] args) {
        int[] arr = {1, 5, 6, 8, 9, 11};
        System.out.println("index == " + fibonacciSearch(arr, 6));
    }

    /**
     * 因为在斐波那契查找算法中,获取mid时需要用到:mid=low+F(k-1)-1,需要使用到斐波那契数列,因此需要先构建一个斐波那契数列
     *
     * @return 使用非递归的方式获取一个斐波那契数列
     */
    public static int[] fib() {
        int[] f = new int[maxSize];
        f[0] = 1;
        f[1] = 1;
        for (int i = 2; i < maxSize; i++) {
            f[i] = f[i - 1] + f[i - 2];
        }
        return f;
    }

    /**
     * @param arr         目标数组
     * @param targetValue 目标值
     * @return 返回目标值对应下标,如果没找到就返回-1
     */
    private static int fibonacciSearch(int[] arr, int targetValue) {
        int low = 0;
        int high = arr.length - 1;
        int k = 0;//表示斐波那契分割数值的下标
        int mid = 0;//存放mid值
        int[] f = fib();//获取斐波那契数列
        //获取菲波那切分割数值的下标
        while (high > f[k] - 1) {
            k++;
        }
        //因为f[k]的值可能大于arr的长度,因此需要使用Arrays工具类,构造一个新的数组,并指向temp[],不足的部分会使用0填充
        int[] temp = Arrays.copyOf(arr, f[k]);
        //实际上需要使用arr数组的最后元素去填充temp数组中0的部分。eg:temp={1,2,3,0,0} =》temp={1,2,3,3,3};
        for (int i = high + 1; i < temp.length; i++) {
            temp[i] = arr[high];
        }
        //使用while来循环处理,找到目标数targetValue
        while (low < high) {
            mid = low + f[k - 1] - 1;
            if (targetValue < temp[mid]) { //此时需要向数组的左边继续查找
                high = mid - 1;
                //为什么要k--:因为全部元素 = 头部元素+尾部元素;f[k]=f[k-1]+f[k-2]
                k--;
            } else if (targetValue > temp[mid]) { //此时需要向数组的右边继续查找
                low = mid + 1;
                k -= 2;
            } else {
                //此时已经找到该值,需要确定,返回的是哪个下标
                if (mid <= high) {
                    return mid;
                } else {
                    return high;
                }
            }
        }
        return -1;
    }
}
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值