算法-查找算法(顺序查找,二分查找,插值查找,斐波那契查找)

线性查找(Sequence Search)

  • 又称线性查找
  • 时间复杂度为线性阶 O(n)

public class SequenceSearchApp {
    /** 顺序查找(线性查找)
     * 从下标0开始逐一对比, 找到了返回下标*/
    public static int seqSearch(int arr[], int value) {
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] == value) {
                return i;
            }
        }

        return -1;
    }

    public static void main(String[] args) {
        int arr[] = {88, 77, 1, 66, -2, 100, 999};
        System.out.println("下标为 " + seqSearch(arr, 77));
    }

}

输出:
> 下标为 1

二分查找(Binary Search)

  • 又称折半查找
  • 原数据必须是有序的
  • 查找公式& 过程:
  1. 公式: mid=(low+high)/2=low+1/2*(high-low)
  2. 原数据: [1, 2, 3, 4, 5]
  3. 首轮 2=(0+4)/2=0+0.5*4
  4. 第二轮 2=(1+3)/2=1+0.5*2 …

public class BinarySearchApp {
    /** 二分查找(当有相同值元素时只会找出其中一个)
     * @param arr 原始数组
     * @param left 左下标
     * @param right 右下标
     * @param value 要查找的值*/
    public static int binarySearch(int arr[], int left, int right, int value){
        if (left > right) {
            return -1;
        }

        int mid = (left + right) / 2;
        int midValue = arr[mid];

        if (midValue < value) { /** 锁定的中间值, 如果小于要查找的值时, 则向右递归进行查找*/
            return binarySearch(arr, mid + 1, right, value);
        } else if (midValue > value) { /** 锁定的中间值, 如果大于要查找的值时, 则向左递归进行查找*/
            return binarySearch(arr, left, mid - 1, value);
        } else {
            /** 找到了, 则返回下标*/
            return mid;
        }
    }

    public static void main(String[] args) {
        int arr[] = {-2, 1, 66, 77, 88, 100, 999};
        System.out.println("下标为 " + binarySearch(arr, 0, arr.length - 1, 66));
    }

}

输出:
> 下标为 2

    /** 二分查找(将所有相同值的下标都返回*/
    public static List<Integer> binarySearch2(int arr[], int left, int right, int value) {
        if (left > right) {
            return new ArrayList<>(0);
        }

        int mid = (left + right) / 2;
        int midValue = arr[mid];

        if (midValue < value) { /** 锁定的中间值, 如果小于要查找的值时, 则向右递归进行查找*/
            return binarySearch2(arr, mid + 1, right, value);
        } else if (midValue > value) { /** 锁定的中间值, 如果大于要查找的值时, 则向左递归进行查找*/
            return binarySearch2(arr, left, mid - 1, value);
        } else {
            /** 当找到了, 不立即返回, 而继续查找*/
            ArrayList<Integer> list = new ArrayList<>();
            /** 从已找出的下标, 向左继续查找*/
            int temp = mid - 1;
            while (true) {
                if (temp < 0 || arr[temp] != value) {
                    break;
                }

                list.add(temp);
                temp -= 1;
            }

            list.add(mid);

            /** 从已找出的下标, 向右继续查找*/
            temp = mid + 1;
            while (true) {
                if (temp < 0 || arr[temp] != value) {
                    break;
                }

                list.add(temp);
                temp += 1;
            }

            return list;
        }
    }

        int arr[] = {-2, 1, 66, 77, 88, 88, 100, 999};
        List<Integer> list = binarySearch2(arr, 0, arr.length - 1, 88);
        System.out.println("下标为 " + list);

输出:
> 下标为 [4, 5]

二分查找(非递归实现)

  • 时间复杂度为线性阶 O(log2n): 即查找目标最多只需 log2n步

public class BinarySearchApp {
    public static void main(String[] args) {
        int[] arr = {1, 3, 8, 10, 11, 67, 100};
        int index = binarySearch(arr,100);
        System.out.println("下标为 " + index);
    }

    /** 二分查找非递归实现*/
    public static int binarySearch(int[] arr,int target) {
        int left = 0;
        int right = arr.length - 1;
        while(left <= right) {
            int mid = (right - left) / 2 + left;
            if(arr[mid] == target) {
                return mid;
            } else if(arr[mid] > target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return -1;
    }

}

输出:
> 下标为 6

插值查找(Interpolation Search)

  • 原数据必须是有序的
  • 查找公式: mid=low+(high-low)*(key-arr[low])/(arr[high]-arr[low])
  • 类似于二分查找, 但每次进行查找会自适应 mid

public class InterpolationSearchApp {
    /** 插值查找
     * @param arr 数组
     * @param left 左边索引
     * @param right 右边索引
     * @param value 查找值*/
    public static int interpolationSearch(int[] arr, int left, int right, int value) {
        /** 为避免越界, 需判断 value < arr[0]和 value > arr[arr.length - 1]*/
        if (left > right || value < arr[0] || value > arr[arr.length - 1]) {
            return -1;
        }

        /** 算出自适应 mid*/
        int mid = left + (right - left) * (value - arr[left]) / (arr[right] - arr[left]);
        int midVal = arr[mid];
        if (value > midVal) {
            return interpolationSearch(arr, mid + 1, right, value);
        } else if (value < midVal) {
            return interpolationSearch(arr, left, mid - 1, value);
        } else {
            return mid;
        }
    }

    public static void main(String[] args) {
        int arr[] = {-2, 1, 66, 77, 88, 88, 100, 999};
        System.out.println("下标为 " + interpolationSearch(arr, 0, arr.length - 1, 88));
    }

}

输出:
> 下标为 4

斐波那契(黄金分割法)查找(Fibonacci Search)

  • 概述: 斐波那契也是更改 mid来进行查找的, mid是通过斐波那契数列来获得, 即 mid=low+F(k-1)-1, 其中 F()是斐波那契数列
  • 斐波那契数列{1,1,2,3,5,8,13,21,34,55…}, 这个数列规则为, 从第二项开始, 每一项都等于前两项之和, 越往后, 就越来越逼近黄金分割0.618, 因此又称为黄金分割数列
  • 原数据必须是有序的

public class FibonacciSearchApp {
    /** 制作斐波那契数列*/
    public static int[] fibonacci() {
        int length = 20;
        int[] f = new int[length];
        f[0] = 1;
        f[1] = 1;
        for (int i = 2; i < length; i++) {
            f[i] = f[i - 1] + f[i - 2];
        }
        return f;
    }

    /** 通过斐波那契数列查找
     * @param arr 数组
     * @param value 查找值*/
    public static int fibonacciSearch(int[] arr, int value) {
        /** 左起始下标*/
        int low = 0;
        /** 右起始下标*/
        int high = arr.length - 1;
        /** 斐波那契数列的下标*/
        int fibonacciKey = 0;
        int mid = 0;
        /** 获取斐波那契数列 [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]*/
        int f[] = fibonacci();
        /** 选取起始黄金分割点下标*/
        while(high > f[fibonacciKey] - 1) {
            fibonacciKey++;
        }
        /** 结果
         * high 7
         * fibonacciKey 5
         * f[fibonacciKey] 8 - 1 = 7
         * 7 > 7 = false*/

        /** 如果起始黄金分割点(f[fibonacciKey] 8)大于原数组长度(high 7), 新生成 temp数组, 长度为黄金分割点 f[fibonacciKey]*/
        int[] temp = Arrays.copyOf(arr, f[fibonacciKey]);
        /** 结果: [-2, 1, 66, 77, 88, 9999, 0, 0] 不足的部分, 使用0填充*/
        for(int i = high + 1; i < temp.length; i++) {
            temp[i] = arr[high];
        }
        /** 结果: [-2, 1, 66, 77, 88, 9999, 9999, 9999] 将所有的填充值0改为, 原数组的最后元素值*/

        while (low <= high) {
            /**
             * 第一轮: mid(4) = low(0) + f[fibonacciKey(5) - 1](5) - 1;
             *  - if (value(77) < temp[mid](88)) {
             *  -   high(3) = mid(4) - 1;
             *  -   fibonacciKey--;
             *  - }
             * 第二轮: mid(2) = low(0) + f[fibonacciKey(4) - 1](3) - 1;
             *  - if (value(77) < temp[mid](66)) {
             *  -   low(3) = mid(2) + 1;
             *  -   fibonacciKey-= 2;
             *  - }
             * 第三轮: mid(3) = low(3) + f[fibonacciKey(2) - 1](1) - 1;
             *  - if (mid(3) <= high(3)) return mid(3);*/
            mid = low + f[fibonacciKey - 1] - 1;
            if(value < temp[mid]) {
                /** mid=f[k-1-1]-1
                 * 将原数组右下标向左移*/
                high = mid - 1;

                fibonacciKey--;
            } else if (value > temp[mid]) {
                /** mid=f[k-2-1]-1
                 * 将原数组左下标向右移*/
                low = mid + 1;

                fibonacciKey -= 2;
            } else {
                /** 找到, 将下标返回*/
                if (mid <= high) {
                    return mid;
                } else {
                    return high;
                }
            }
        }

        return -1;
    }

    public static void main(String[] args) {
        int[] arr = {-2, 1, 66, 77, 88, 9999};
        System.out.println("下标为 " + fibonacciSearch(arr, 77));
    }

}

输出:
> 下标为 3

如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!

©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页