四大查找方法

查找算法解析:顺序、二分、插值与斐波那契
本文介绍了四种查找算法:顺序查找、二分查找的两种应用、插值查找及其适用场景,以及未展开讨论的斐波那契查找。对于数据量大且关键字分布均匀的查找表,插值查找可能优于折半查找。


#四大查找方法
java 中常用的查找的四种

  • 1.顺序查找
  • 2.二分查找
  • 3.插值查找
  • 4.斐波那契查找

1 顺序查找

  /**
     * 顺序查找
     *
     * @param arr
     * @param value
     * @return
     */
    public static int seqSearch(int[] arr, int value) {
        //找到返回下标,没找到返回 -1
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == value) {
                return i;
            }
        }
        return -1;
    }

2 二分查找

1.二分查找 返回一个值

    /**
     * 二分查找
     * 1.首先确定该数组的中间的下标
     * mid = (left + right)/2
     * 2.然后让查找的数据 findVal与arr[mid] 进行比较
     * 2.1 findVal> arr[mid] 说明查找的数在右边,故在右边递归查找
     * 2.2 findVal> arr[mid] 说明查找的数在左边,故在向左边递归查找
     * 2.3 findVal == arr[mid] 说明找到了,直接返回
     * <p>
     * //递归说明时候结束
     * 结束条件为
     * 1) 找到了
     * 2) 整个完整数组,都没有找到,结束条件为 left>right
     *
     * @param arr     查找的数组
     * @param left    左边索引
     * @param right   右边索引
     * @param findVal 要查找的数
     * @return
     */
    public static int binarySearch(int[] arr, int left, int right, int findVal) {
        //当left>right时,说明整个数组没有找想要的值
        if (left > right) {
            return -1;
        }

        int mid = (left + right) / 2;
        int midVal = arr[mid];
        if (findVal > midVal) {
            return binarySearch(arr, mid + 1, right, findVal);
        } else if (findVal < arr[mid]) {
            return binarySearch(arr, left, mid - 1, findVal);
        } else {
            return mid;
        }

    }

2.二分查找所有值

    //查找所有相同的值
    public static List<Integer> binarySearchAll(int[] arr, int left, int right, int findVal) {
        //当left>right时,说明整个数组没有找想要的值
        if (left > right) {
            return new ArrayList<Integer>();
        }

        int mid = (left + right) / 2;
        int midVal = arr[mid];
        if (findVal > midVal) {
            return binarySearchAll(arr, mid + 1, right, findVal);
        } else if (findVal < arr[mid]) {
            return binarySearchAll(arr, left, mid - 1, findVal);
        } else {
            /**
             * 1.在找到mid 索引值,暂时不马上返回.
             * 2.先向 mid 的左边 进想递归扫描 将所有等于查找值 findVal 的元素的下标,添加到List中,
             * 3.先向 mid 的右边 进想递归扫描 将所有等于查找值 findVal 的元素的下标,添加到List中,
             * 4. 最后将list返回
             */


            ArrayList<Integer> reslist = new ArrayList<>();

            //2先向 mid 的左边 进想递归扫描 将所有等于查找值 findVal 的元素的下标,添加到List中
            int temp = mid -1;
            while(true){
                if(temp < 0 || arr[temp] != findVal){// temp < 0 说明左边数组没有了, arr[temp] != findVal 说明左边没有查找的值了
                    break;
                }
                //否则,就将temp存到list
                reslist.add(temp);
                temp -= 1;//向左递归

            }
            //3.先向 mid 的右边 进想递归扫描 将所有等于查找值 findVal 的元素的下标,添加到List中,
            temp = mid + 1;
            while(true){
                if(temp > arr.length -1 || arr[temp] != findVal){// temp < arr.length -1 说明右边数组没有了, arr[temp] != findVal 说明右边没有查找的值了
                    break;
                }
                //否则,就将temp存到list
                reslist.add(temp);
                temp += 1;//向右递归

            }
            //4. 最后将list返回
            return reslist;

        }

    }

3 插值查找算法


    /**
     * 插值查找算法
     * @param arr 要查找的有序数组
     * @param left
     * @param right
     * @param findVal
     * @return 找到返回下标,找不到返回 -1
     */
    public static int insertValueSearch(int[] arr, int left, int right, int findVal) {

        System.out.println("插值查找次数...");

        //注意: findVal < arr[0] 与 findVal > arr[arr.length -1 ] 必须满足, 否则我们得到的mid可能越界
        if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) {
            return 1;
        }

        int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);
        int midVal = arr[mid];
        if(findVal > midVal){
            return insertValueSearch(arr,mid + 1,right,findVal);
        }else if (findVal < midVal){

            return insertValueSearch(arr,left,mid -1,findVal);
        }else{

            return mid;
        }
    }

    public static void main(String[] args) {

        int arr[] = {12,345,243,65,3534,246,354,25,676,87};

        //int index = insertValueSearch(arr, 0, arr.length - 1, 3534);
        int index = binarySearch11(arr, 0, arr.length - 1, 3534);
        System.out.println(index);

    }

注意:

1 ) 对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找, 速度较快.

2 ) 关键字分布不均匀的情况下,该方法不一定比折半查找要好

4 斐波那契查找 待续

import java.util.Arrays;

public class FibonacciSearch {


    public static int maxSize = 20;

    public static void main(String[] args) {
        int[] arr = {1, 5, 18, 100, 189, 1860, 1234};
        System.out.println("index=" + fibSearch(arr, 189));
    }
    //因为后面我们mid=low+F(k- 1 )- 1 ,需要使用到斐波那契数列,因此我们需要先获取到一个斐波那契数列
    //非递归方法得到一个斐波那契数列

    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;

    }

    //编写斐波那契查找算法
    //使用非递归的方式编写算法

    /**
     * @return 返回对应的下标,如果没有- 1
     * @parama 数组
     * @paramkey 我们需要查找的关键码(值)
     */

    public static int fibSearch(int[] a, int key) {

        int low = 0;

        int high = a.length - 1;

        int k = 0;//表示斐波那契分割数值的下标

        int mid = 0;//存放mid值

        int f[] = fib();//获取到斐波那契数列

    //获取到斐波那契分割数值的下标

        while (high > f[k] - 1) {

            k++;

        }

    //因为 f[k] 值 可能大于 a 的 长度,因此我们需要使用Arrays类,构造一个新的数组,并指向temp[]

    //不足的部分会使用 0 填充

        int[] temp = Arrays.copyOf(a, f[k]);

        //实际上需求使用a数组最后的数填充 temp

        for (int i = high + 1; i < temp.length; i++) {

            temp[i] = a[high];

        }

        // 使用while来循环处理,找到我们的数 key

        while (low <= high) {// 只要这个条件满足,就可以找

            mid = low + f[k - 1] - 1;

            if (key < temp[mid]) {//我们应该继续向数组的前面查找(左边)

                high = mid - 1;

                //为甚是 k--

                //说明

                // 1. 全部元素 =前面的元素 + 后边元素

                // 2 .f[k]=f[k- 1 ]+f[k- 2 ]

                //因为 前面有 f[k- 1 ]个元素,所以可以继续拆分 f[k- 1 ]=f[k- 2 ]+f[k- 3 ]

                //即 在 f[k- 1 ] 的前面继续查找 k--

                //即下次循环 mid=f[k- 1 - 1 ]- 1

                k--;

            } else if (key > temp[mid]) {// 我们应该继续向数组的后面查找(右边)

                low = mid + 1;

                //为什么是k-= 2

                //说明

                // 1. 全部元素 =前面的元素 + 后边元素

                // 2 .f[k]=f[k- 1 ]+f[k- 2 ]

                // 3. 因为后面我们有f[k- 2 ] 所以可以继续拆分 f[k- 1 ]=f[k- 3 ]+f[k- 4 ]

                // 4. 即在f[k- 2 ] 的前面进行查找 k-= 2

                // 5. 即下次循环 mid=f[k- 1 - 2 ]- 1

                k -= 2;

            } else {//找到

                //需要确定,返回的是哪个下标

                if (mid <= high) {

                    return mid;

                } else {

                    return high;

                }

            }
        }
        return -1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值