常用的四种查找算法总结

1.线性查找:

线性查找常用于判断数组中是否有要查找的值,通过直接遍历数组,判断元素是否相等即可。由于线性查找很简单,直接贴代码。

 这里的代码只实现了,查找第一个元素的下标,如果找到所有元素可以定义一个集合,将return i;改成向集合中添加元素即可。

public class SeqSearch {
    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 = {1,9,11,-1,34,89};
        int index = seqSearch(arr,-1);
        if (index == -1){
            System.out.println("没有找到");
        }else {
            System.out.println("待查找元素下标为:"+index);
        }
    }
}

2.二分查找:

 二分查找的前提是数组有序

二分查找的思路是找到数组的中间下标,将待查找元素和中间下标对应值比较大小,大的话往数组右边查找,小的话往数组左边查找,直到找到为止,返回中间下标对应的元素值即可。

步骤:

  1. 首先确定数组的下标,mid = (left+right)/  2;
  2. 带查找数findVal和arr[mid]比较,arr[mid]<findVal,查找的数在数组右边,向右递归查找,反之向左,相等则刚好找到;
  3. 递归结束的条件,找到就结束递归,找不到则left>right,递归退出。
import java.util.ArrayList;
import java.util.List;

public class BinarySearch {
    /**
     * @param arr       带查找数组
     * @param left      左边的索引
     * @param right     右边的索引
     * @param findvalue 带查找的值
     * @return 待查找元素的第一个下标值
     */
    public static int binarySearch(int[] arr, int left, int right, int findvalue) {
        if (left > right) { // 没有找到
            return -1;
        }
        int mid = (left + right) / 2;
        if (arr[mid] > findvalue) {
            // 向左递归
           return binarySearch(arr, left, mid - 1, findvalue); // 注意left和right的值,在递归时要改变
        } else if (arr[mid] < findvalue) {
            // 向右递归
           return binarySearch(arr, mid + 1, right, findvalue);
        } else {
            return mid;
        }
    }

    // 查找到待查找元素所有下标
    public static List<Integer> binarySearch2(int[] arr, int left, int right, int findvalue) {
        if (left > right) { // 没有找到
            return null;
        }
        int mid = (left + right) / 2;
        if (arr[mid] > findvalue) {
            // 向左递归
            return binarySearch2(arr, left, mid - 1, findvalue); // 注意left和right的值,在递归时要改变
        } else if (arr[mid] < findvalue) {
            // 向右递归
            return binarySearch2(arr, mid + 1, right, findvalue);
        } else {
            // 待查找元素所有下标
            List<Integer> list = new ArrayList<Integer>();
            // 向左找
            int temp = mid-1;
            while (true){
                // 退出条件:下标越界或者没有findvalue
                if (temp<0 || arr[temp] != findvalue){
                    break;
                }
                list.add(temp); // 存入下标
                temp--;
            }
            // 将mid加入
            list.add(mid);
            // 向右找
            temp = mid+1;
            while (true){
                if (temp >arr.length-1 || arr[temp] != findvalue){
                    break;
                }
                list.add(temp); // 存入下标
                temp++;
            }

            return list;
        }
    }

    public static void main(String[] args) {
        // 只能找到一个待查找元素的下标,注意数组元素要有序!!!
        // int[] arr = {1, 9,89, 11, -1, 34, 89};
        int[] arr = {-1,1,9,11,34,89,89};
        int index = binarySearch(arr,0,arr.length-1,89);
        System.out.println("待查找元素下标:"+index);

        // 找到所有待查找元素的下标
        int[] arr2 = {-1,1,1,1,9,9,9,11,34,89,89};
        List<Integer> indexs = binarySearch2(arr2,0,arr2.length-1, 89);
        System.out.println("待查找元素下标:"+indexs);

    }
}

3.插值查找:

插值查找的前提是数组有序。  

插值排序类似于二分查找,不同的是插值查找的中间元素下标mid不同,它采用的是自适应mid。采用插值查找的好处是可以减少二分查找中递归的次数。比如arr={1,2,3...98,99,100},我们要找到100,那么需要递归很多次,但是采用插值查找,计算自适应mid=0+(100-1)/(100-1)= 99,那么从arr[99]开始查找,由此可以看出插值查找的优越性。

接下来还是用代码实现这个算法,主要是把mid换掉,递归退出条件修改一下即可。

public class InsertValueSearch {
    public static int insertValueSearch(int[] arr, int left, int right, int findValue) {
        // findValue<arr[0] 和 findValue>arr[arr.length-1] 必须需要,
        // 否则mid公式计算的mid结果可能导致arr[mid]越界
        if (left > right || findValue<arr[0] || findValue>arr[arr.length-1]) { // 没有找到
            return -1;
        }

        int mid = left + (findValue-arr[left])/(arr[right]-arr[left])*(right-left);
        if (arr[mid] > findValue) {
            // 向左递归
            return insertValueSearch(arr, left, mid - 1, findValue); // 注意left和right的值,在递归时要改变
        } else if (arr[mid] < findValue) {
            // 向右递归
            return insertValueSearch(arr, mid + 1, right, findValue);
        } else {
            return mid;
        }
    }

    public static void main(String[] args) {
        int[] arr = {-1,1,9,11,34,89,89};
        int index = insertValueSearch(arr,0,arr.length-1,89);
        System.out.println("待查找元素下标:"+index);
    }
}

4.斐波那契查找:

斐波那契查找的前提是数组有序

斐波那契查找又叫做黄金分割法查找,复习一下黄金分割点。

黄金分割点是指把一条线段分割为两部分,其中一部分与全长之比等于另一部分与这部分之比,比值近似0.618。

那么知道了黄金分割点,肯定要和斐波那契数列特点联合起来,再来看看斐波那契数列特点。

斐波那契数列特点:第一项=1,第二项=1,第三项=第一项+第二项=2,第四项=第二项+第三项=3,第n项=第n-1项+第n-2项。

斐波那契英文:Fibonacci = f(k) = f(k-1)+f(k-2)= {1,1,2,3,5,8,13,21,34,55,89.......}。

那么怎么黄金分割和斐波到低啥关系呢?13/21,21/34,34/55近似0.618,斐波那契数列两个项比值近似黄金比例

知道了关系,肯定要在查找中使用,斐波那契查找就是运用了黄金分割法,将数组中的中间下标mid放置到了黄金分割点附近。

上原理图:

mid=low+F(k-1)-1,F(k)代表斐波那契数列,由F(k)-1=(F(k-1)-1)+ (F(k-2)-1)+ 1,可以得到F(k-1)-1位于F(k)-1,(F(k-2)-1)+ 1,取出斐波那契数列中的值带入公式来特殊化理解,34,21,13分别对应公式中的三项,13/21,21/34,相当于ac=21 , bc = 13 ,ac =34,ac/ab=bc/ac=0.618,所以21相当于黄金分割点,21也就是F(k-1)-1相当于黄金分割点

为什么F(k-1)要减一呢?这个应该下标从0开始,mid取值也要包括0,因此减1,这是我自己的理解,不知道不对不对。

所以,mid=low+F(k-1)-1

在子段递归中mid也是这样取法。

还要注意在使用斐波那契查找时要进行数组扩容,原因见下图。

import java.util.Arrays;

public class FibonacciSearch {
    public static int maxSize = 20;

    /**
     * @param arr 待查找数组
     * @param key 要查找的值
     * @return 返回找到的下标
     */
    public static int fibSearch(int[] arr, int key) {
        int low = 0;
        int high = arr.length - 1;
        int k = 0; // 表示斐波那契分割数值的下标
        int mid = 0;
        int[] f = fib(); // 获取fib数组
        // 数组扩容,获取到斐波那契数列分割数值下标
        while (high > f[k] - 1) {
            k++;
        }
        // 因为f[k]的值可能大于a的长度,因此我们需要用Arrays类,构造一个新的数组,并指向temp[]
        // 不足部分0填充
        int[] temp = Arrays.copyOf(arr, f[k]);

        // 我们不用0填充,用arr[high]来填充
        for (int i = high + 1; i < temp.length; i++) {
            temp[i] = arr[high]; // 将temp数组对应arr长度之后的下标后面的元素全都赋值为arr[high]
        }

        // 用while循环找到要找到的key
        while (low <= high) {
            mid = low + f[k - 1] - 1;
            if (key < temp[mid]) { //向左查找
                high = mid - 1;
                k -= 1; // k--的目的在于找到前面的黄金分割点,也就是找到新的mid
            } else if (key > temp[mid]) {//向右查找
                low = mid + 1;
                k -= 2;
                /*
                k-=2的原因:f[k] = f[k-1] + f[k-2]===> f[k-2] = f[k-3] - f[k-4] ===>黄金分割点f[k-2]
                在f[k-2]的前面查找,所以下次循环mid = f[k-1-2]-1
                 */
            } else {
                if (mid <= high) {
                    return mid;
                } else {
                    return high;
                }
            }
        }
        return -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;
    }

    public static void main(String[] args) {
        int[] arr = {-1, 1, 5, 9, 11, 34, 89, 89};
        int index = fibSearch(arr, 34);
        System.out.println("待查找元素下标:" + index);
    }
}

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值