韩顺平三种查找,二分插值斐波

感觉老韩斐波那契查找法这里讲得不好,听得我一头雾水的,去找其他人视频讲解才勉强理解

package org.example._10查找;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


public class Search {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6, 6, 6, 7, 8, 9};
        //List<Integer> b_result = BinarySearch(arr, 0, arr.length - 1, 1);
        //List<Integer> i_result = insertValueSearch(arr, 0, arr.length - 1, 5);
        int f_result = fibSearch(arr, 9);
        System.out.println(f_result);
    }

    //二分查找的前提是数组有序,返回的是要找的数的下标,注意要解决目标数重复的问题
    public static List<Integer> BinarySearch(int[] arr, int left, int right, int findVal) {
        System.out.println("HELLO_BINARY");
        int mid = (left + right) / 2; //中点下标  mid = left + (right-left) * 1/2
        int midVal = arr[mid]; //中点值

        //这里不像插值查找需要findVal < arr[0] || findVal > arr[arr.length - 1]
        //因为mid公式中不包含findVal
        if (left > right) {
            return new ArrayList<Integer>(); //没找到就返回一个空list
        }
        if (findVal < midVal) { //递归向左查找, 注意要return
            return BinarySearch(arr, left, mid - 1, findVal);
        } else if (findVal > midVal) { //递归向右查找
            return BinarySearch(arr, mid + 1, right, findVal);
        } else {
            List<Integer> list = new ArrayList<>(); //list用来存储结果,因为要找的数可能重复出现
            //对于一个二分查找序列,因为一定是有序的,所以目标值如果重复了就一定是连续的
            //比如 1,2,3,3,3,4,5,我要找3,那么其中中间的那个3一定最后会是mid,这时就要扫描这个mid左右两边的情况
            int temp = mid - 1;  //扫描mid左边
            while (true) {
                //如果temp越界说明要找的数是数组第一个,左边第一个不是要找的数,都说明要找的数左边没有重复
                if (temp < 0 || arr[temp] != findVal) break;
                list.add(temp--); //如果有就放进list,然后指针左移
            }
            list.add(mid); //自然要把最终结果mid也放进list
            temp = mid + 1; //然后扫描mid右边的情况
            while (true) {
                //同样的如果temp越界说明mid是最后一个数,如果arr[temp]找不到说明右边没有重复,都说明右边没有重复
                if (temp > arr.length - 1 || arr[temp] != findVal) break;
                list.add(temp++);
            }
            return list;
        }
    }

    //插值查找.数据量比较大,元素值分布比较均匀跳跃性不大,和二分查找的区别就是换一下mid的公式
    public static List<Integer> insertValueSearch(int[] arr, int left, int right, int findVal) {
        System.out.println("HELLO_INSERT");
        int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]); //插值点
        int midVal = arr[mid]; //插值点值

        //mid公式中包含findVal,导致mid的值可能很大,导致越界,所以加判断条件
        if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) {
            return new ArrayList<>(); //没找到就返回一个空list
        }
        if (findVal < midVal) { //递归向左查找
            return insertValueSearch(arr, left, mid - 1, findVal);
        } else if (findVal > midVal) { //递归向右查找
            return insertValueSearch(arr, mid + 1, right, findVal);
        } else {
            List<Integer> list = new ArrayList<>(); //list用来存储结果,因为要找的数可能重复出现
            //对于一个二分查找序列,因为一定是有序的,所以目标值如果重复了就一定是连续的
            //比如 1,2,3,3,3,4,5,我要找3,那么其中中间的那个3一定最后会是mid,这时就要扫描这个mid左右两边的情况
            int temp = mid - 1;  //扫描mid左边
            while (true) {
                //如果temp越界说明要找的数是数组第一个,左边第一个不是要找的数,都说明要找的数左边没有重复
                if (temp < 0 || arr[temp] != findVal) break;
                list.add(temp--); //如果有就放进list,然后指针左移
            }
            list.add(mid); //自然要把最终结果mid也放进list
            temp = mid + 1; //然后扫描mid右边的情况
            while (true) {
                //同样的如果temp越界说明mid是最后一个数,如果arr[temp]找不到说明右边没有重复,都说明右边没有重复
                if (temp > arr.length - 1 || arr[temp] != findVal) break;
                list.add(temp++);
            }
            return list;
        }
    }

    //斐波那契查找法
    //1.获取斐波那契数列
    public static int[] fib() {
        int[] f = new int[20]; //长度为20时斐波那契数列可以达到6765
        f[0] = 1;
        f[1] = 1;
        for (int i = 2; i < f.length; i++) {
            f[i] = f[i - 1] + f[i - 2];
        }
        return f;
    }

    //2.查找
    public static int fibSearch(int[] arr, int findVal) {
        int low = 0;  //left
        int high = arr.length - 1; //right
        int k = 0; //mid的辅助值
        int mid = 0; //用于查找的核心值
        int f[] = fib();
        //数组长度得先>=f[k]-1才能使用该方法查找,就是要找到最接近数组长度的那个斐波那契数
        //这个数的值就是新数组的长度,这个数在斐波那契数列中的下标就是k
        //比如n=10,那么最接近它的是13,13在斐波那契数列中下标是6,[1 1 2 3 5 8 13]
        //所以new_length是13,而k=6
        while (high > f[k] - 1) {
            k++;
        }
        int[] temp = Arrays.copyOf(arr, f[k]);
        //新数组中多出来的数得和原数组末尾数一样
        for (int i = high + 1; i < temp.length; i++) {
            temp[i] = arr[high];

        }
        //开始在新数组中找数
        //为什么要搞一个新数组?因为比如当前新数组长度是13,因为是满足黄金分割比0.618的
        //所以这个数组就可以分成前面8个和后面5个
        //就可以按照斐波那契数列来进行分割和查找,以此类推
        while (low <= high) {
            mid = low + f[k - 1] - 1;//公式
            //如果要找的数小于中间值,就往左找
            if (findVal < temp[mid]) {
                high = mid - 1;
                k--; //前面是8个,那么因为[5,8,13],k就要从13这里跑到8这里,则k的下标减一
                //如果要找的数大于中间值,就往右找
            } else if (findVal > temp[mid]) {
                low = mid + 1;
                k -= 2; //跑到mid右边,k往前走两步
            } else {
                if (mid <= high) {
                    return mid; //如果mid和high相等那么mid就是对应值的下标
                } else {
                    return arr.length - 1;//如果mid>high,那么说明要找的值是最后一个数,同时也是扩充的数
                }
            }
        }
        return -1;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值