数据结构算法爬坑日记九

常用四种
 ** 线性(顺序)查找:就是将需要查找的数列进行遍历挨个比对即可 (无序有序均可)
 ** 二分查找/折半查找 (需要在有序的前提下)
 ** 插值查找 (需要有序)
 ** 斐波那契查找 (需要有序)

二分查找

思路:将数列的中间值与需要查找的值进行比较,如果大于,则向左递归,如果小于,则向右递归,分别再进行与中间
值的对比,循环往复,直到找到这个数,将其位置返回或者循环结束返回没有找到

代码实现
//二分查找
public class BinarySelect {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 3, 3, 8, 9, 10, 545};
        System.out.println(select(arr, 0, arr.length - 1, 3));
    }

    //arr需要查找的数组 left 左临界索引 right 右临界索引 value 需要查找的值
    public static List<Integer> select(int[] arr, int left, int right, int value) {
        int midden = (left + right) / 2;//中间索引
        int middenValue = arr[midden];//中间值
        List<Integer> indexList = new ArrayList<>();//存放索引的集合
        //如果左索引大于右索引,没有找到,查找结束
        if (left > right) {
            return indexList;
        }
        if (middenValue > value) {
            //中间值大于需要查找的值向左递归
            return select(arr, left, midden - 1, value);
        } else if (middenValue < value) {
            //中间值小于需要查找的值向右递归
            return select(arr, midden + 1, right, value);
        } else {
            //找到后向左查看是否还有此值,并将索引添加到索引集合
            int temp = midden - 1;
            while (true) {
                if (temp < 0 || arr[temp] != value) {
                    break;
                } else {
                    indexList.add(0, temp);
                    temp--;
                }
            }
            //将原来的索引加入到索引集合
            indexList.add(midden);
            //向右查看是否还有此值,并将索引添加到索引集合
            temp = midden + 1;
            while (true) {
                if (temp > arr.length - 1 || arr[temp] != value) {
                    break;
                } else {
                    indexList.add(temp);
                    temp++;
                }
            }
            return indexList;
        }
    }
}

插值查找

一个线性增长数列,如果需要查找的值位于左边的话,二分查找会查找很多次,而插值查找可以通过自适应获取中值
来减少查找次数,其它与二分查找一致
中值获取公式:midden = left + (right - left) * (value - arr[left]) / (arr[right] - arr[left])

代码示例(以查找到一个就返回索引为例,返回多个与二分查找一致)
//插值查找
public class InsertSelect {
    public static void main(String[] args) {
        int[] arr = new int[100];
        for (int i = 0; i < 100; i++) {
            arr[i] = i + 2;
        }
        System.out.println(select(arr, 0, arr.length - 1, 2));
    }

    public static int select(int[] arr, int left, int right, int value) {
    //防止查的数过大越界异常,后俩个判断必须加的
        if (left > right || value > arr[right] || value < arr[left]) {
            return -1;
        }
        int midden = left + (right - left) * (value - arr[left]) / (arr[right] - arr[left]);//中间值
        int middenValue = arr[midden];
        if (value > middenValue) {
            //向右递归
            return select(arr, midden + 1, right, value);
        } else if (value < middenValue) {
            //向左递归
            return select(arr, left, midden - 1, value);
        } else {
            //找到了
            return midden;
        }
    }
}

斐波那契查找

算法思想:相对于二分查找,仅改变了获取中值的方法,借助斐波那契数列的特殊性质(每一项都等于前两项的和)
将原数列分为左右两项,进行比对,根据比对结果再向左右一边继续比对,直到找到或者没找到,算法结束
中值获取:midden=left+f(k-1)-1 // left为此次比对的左索引,f()为一个斐波那契数列

代码实现(找到一个即返回,查找全部相同值与二分法类似)
//斐波那契查找
public class FibonacciSelect {
    public static void main(String[] args) {
        int[] arr = {1, 2, 6, 8, 10, 16, 18, 26};
        System.out.println(select(arr, 18));
    }

    //定义一个斐波那契数列(长度视情况而定)
    public static int[] fibonacci() {
        int[] fibonacciArray = new int[20];
        fibonacciArray[0] = 1;
        fibonacciArray[1] = 1;
        for (int i = 2; i < fibonacciArray.length; i++) {
            fibonacciArray[i] = fibonacciArray[i - 1] + fibonacciArray[i - 2];
        }
        return fibonacciArray;
    }

    //查找
    public static int select(int[] arr, int value) {
        int left = 0;//左索引
        int right = arr.length - 1;//右索引
        int k = 0;//斐波那契数列对应下标
        int[] f = fibonacci();//获取一个斐波那契数列
        int midden;//中值
        //找到原数组对应斐波那契数列的项,小于等于即可
        while (f[k] < arr.length) {
            k++;
        }
        //将原来数组拷贝一份并扩容至斐波那契数列对应项的长度,多余部分用最后一项填充,扩容的原因参考返回值
        int[] temp = Arrays.copyOf(arr, f[k]);
        for (int i = arr.length + 1; i < temp.length - arr.length; i++) {
            temp[i] = arr[right];
        }
        while (left <= right) {
            midden = left + f[k - 1] - 1;
            //如果查找值小于中值,向左查找
            if (value < temp[midden]) {
                right = midden - 1;
                k--;
            } else if (value > temp[midden]) {
                //如果查找值大于中值,向右查找
                left = midden + 1;
                k -= 2;
            } else {
                //已经找到,返回midden和right二者较小值
                //因为我们已经对数组进行扩容,在取中值与查找值进行比对时,可能会出现下标越界(相对于原来的数组)
                //如果越界了,而此时又正好与查找的值相等,因为后面的值都等于最后一项,直接返回最后一项下标
                return Math.min(midden, right);
            }
        }
        //循环结束执行到这的话就是没找到
        return -1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值