寻找算法 2020-09-22


如何快速的在一个有序列表中找到对应的数

一、二分法查找

示例:每次都拆半,要是查找的值大于中间值,说明在右边,就像右边递归,或者相反,在找到值了以后,可以判断下左右两边有没有相同的值.

public class BinarySearch {
    private static List<Integer> list = new ArrayList<>();
    public static void main(String[] args) {
        int[] a = {0,1,2,3,4,6,7,9,10,60,60};
        List<Integer> list = binarySearch(a, 0, a.length - 1,60);
        if (list.size() == 0) {
            System.out.println("要查找的值不在数组中");
            return;
        }
        System.out.print("要查找的值下标为:" );
        for (Integer integer : list) {
            System.out.print(integer + " ");
        }
    }

    /**
     *
     * @param a 查找的数组
     * @param left 左索引
     * @param right 右索引
     * @param target 要查找的值
     */
    public static List<Integer> binarySearch(int[] a, int left, int right, int target) {
        int mid = (left + right) / 2;
        //每次传入的值都会mid + 1 或者 mid- 1,当左边索引大于右边索引以后,说明数组中没有要找的值
        if (left > right || a.length == 0) {
            return null;
        }
        if (target > a[mid]) {
            //向右递进
            binarySearch(a,mid + 1,right,target);
        }else if (target < a[mid]) {
            //向左递进
            binarySearch(a,left,mid - 1,target);
        }else {
            list.add(mid);
            int m = mid + 1; //临时变量
            //向右寻找有没有相同的,小心索引越界
            while ( m <= right  && target == a[m] ){
                list.add(m);
                m++;
            }
            m = mid - 1;
            //向左寻找有没有相同的
            while (left <= m && target == a[m] ) {
                list.add(m);
                m--;
            }
        }
        return list;
    }
}

二、插值查找

插值查找实际上是利用
mid =

计算查找值在数组中的比例,得出大概的最低位置,然后以这个大概位置去找.面对数据相差不大的比较好点,数据相差太大就,找到的位置就不是很准.

public class InsertValueSearch {
    private static List<Integer> list = new ArrayList<>();
    public static void main(String[] args) {
        int[] a = {0,1,2,3,4,6,7,9,10,60,60};
        try {
            insertValueSearch(a, 0, a.length - 1, 0);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        if (list.size() == 0) {
            System.out.print("要找的数不在数组中");
            return;
        }
        System.out.print("要找的数的下标为:");
        for (Integer integer : list) {
            System.out.print(integer);
        }
    }

    public static void insertValueSearch(int[] a, int left, int right, int target)  {
        if (a == null) {
            throw new NullPointerException("传入的数组是空的");
        }
        //如果大于最大值,说明肯定不在了,或者数组长度为0;
        if (target > a[right] || a.length == 0) {
            return;
        }
        //假如最大值-最小值等于0,说明是同一个数
        if (a[right] - a[left] == 0) {
            if (target == a[left]) {
                list.add(1);
                System.out.println("整个数组都是要找的值");
            }
            return;
        }
        //大概位置 = 最低位置 + 总数 * 百分比
        int mid = left + (right - left) * (target - a[left]) / (a[right] - a[left]);
         //向左边查找
        int m = mid;
        while (left <= m && target == a[m] ) {
            list.add(m);
            m--;
        }
        //向右边查找
        m = mid + 1;
        while (m <= right && target == a[m]) {
            list.add(m);
            m++;
        }
    }
}

斐波那契查找

通过一组前面一个除以后面一个逐渐近似黄金分割比例的数组来作为查找的依据。
在这里插入图片描述

public class FibSearch {
    private static List<Integer> list = new ArrayList<>();

    public static void main(String[] args) {
        int[] a = {0, 1, 2, 3, 4, 6, 7, 9, 10, 60, 60};
        fibSearch(a,6);
        if (list.size() == 0) {
            System.out.print("找不到需要的值");
            return;
        }
        System.out.print("查找值的下标为:");
        for (Integer integer : list) {
            System.out.print(integer + " ");
        }
    }

    public static int[] fib() {
        int[] fib = new int[20];
        fib[0] = 1;
        fib[1] = 1;
        for (int i = 2; i < fib.length; i++) {
            fib[i] = fib[i - 2] + fib[i - 1];
        }
        return fib;
    }

    public static void fibSearch(int[] a, int target) {
        //目前的高位,和地位所在索引
        int high = a.length - 1;
        int low = 0;
        //创建黄金分割组
        int[] fib = fib();
        int mid = 0; // 大概位置
        int k = 0; // 黄金数组下标

        //黄金分割比中,fib[n] = fib[n-1] + fib[n-2]
        //但他是从1开始的 所以我们左右两边都减1
        /**
         * fib[n] - 1 :代表整个数组的最大下标
         * fib[n-1] - 1 :代表是我们数组中前面更大的那一块,留出了一个mid值
         * fib[n-2] - 1: 代表的后面比较小的那一块
         * + 1 其实刚好是留下的mid的一个下标
         */
        // fib[n] - 1 = (fib[n-1] - 1) + (fin[n-2] - 1) + 1
        //判断最大的黄金分割数的值是多少
        while (a.length > (fib[k] - 1)) {
            k++;
        }
        //扩充a数组的长度到fib大小,用最大值填充到fib的长度,是为了防止超过了a的索引
        int[] temp = Arrays.copyOf(a, fib[k]);
        for (int i = high + 1; i < fib[k]; i++) {
            temp[i] = a[high];
        }
        //low超过了high都没找到的话就没在里面了
        while (low <= high) {
            // - 1是因为下标从0开始
            mid = low + fib[k - 1] - 1;
            //如果小于mid,说明在左边即,长的那段fib[k - 1]那段
            if (target < temp[mid]) {
                high = mid - 1;
                //再次缩小范围 fib[k - 1] = fib[k - 2] + fib [k - 3];
                k -= 1;
            } else if (target > temp[mid]) {
                //如果大于的话就在fib[k - 2]那段
                low = mid + 1;
                //fib[k - 2] = fib[k - 3] + fib[k - 4]
                k -= 2;
            } else {
                //如果找到的超过了数据最大,说明可能有
                if (mid > a.length - 1) {
                    list.add(mid);
                    //注意 有可能low等于high的时候才能找到我们需要的时候的值,所以找到了要记得退出
                    return;
                } else {
                    list.add(mid);
                    //向左遍历,至少在high右边
                    int m = mid + 1;
                    while (m <= high && target == a[m]) {
                        list.add(m);
                        m++;
                    }
                    m = mid - 1;
                    //至少大于low
                    while (m >= low && target == a[m]) {
                        list.add(m);
                        m--;
                    }
                    return;
                }

            }
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值