(Java实现)查找算法---线性、折半、插值、斐波那契

宝蟾
• 阅读 3426

查找算法

线性查找

  • 基本思路:线性查找是最简单的查找方法,也很好理解,就是对一组有序/无序的序列进行遍历,逐个比较,直到找到对应的值
  • 代码如下
public class LinearSearch {
    /**
 * * @param arr 要查找的数组
 * @param findVal 要查找的值
 * @return 返回对应值的下标,如果没有返回-1
 */ public static int linearSearch(int[] arr, int findVal){
        if (arr == null || arr.length <= 0 ) return -1;
 for (int i = 0; i < arr.length; i ++){
            if (arr[i] == findVal)
                return i;
 }
        return -1;
 }
    public static void main(String[] args) {
        int[] arr = {128,321,23,4,19,23,10,98,100};
 int result = linearSearch(arr, 10);
 System.out.println(result);
 }
}

折半查找

  • 介绍:折半查找也叫二分查找,要实现折半查找必须满足两个要求,第一是数列要有序的,第二是存放数列的容器是按序存放的
  • 基本思路

    • 1)确定数组的中间下标mid,等于左边下标left+右边下标right的一半,mid = (left + right) / 2
    • 2)把要找的值findVal和下标mid对应的值midVal进行比较

      • 如果findVal大于midVal,则表明要找的数在右边,向右查找,此时的left为mid + 1,right不变,重新计算mid
      • 如果findVal小于midVal,则表明要找的数在左边,向左查找,此时的right为mid - 1,left不变,重新计算mid
      • 如果上面两个条件都不满足,证明findVal等于midVal,则直接返回mid下标
    • 3)如果说left > right,证明我们要找的数不存在,直接返回-1
  • 举例说明:比如有序数组{1,8,9,10,29,39,49,69},查找39

    • (Java实现)查找算法---线性、折半、插值、斐波那契如上图所示,第一次并没有找到findVall,第二次left,mid重新调整过后,找到了findVal
  • 代码如下

    • 这里使用的是递归的方式实现折半查找
/**
 * 使用折半查找的前提是该数组是有序的
 */
public class BinarySearch {
    public static int binarySearch(int[] arr, int findVal){
        if (arr == null || arr.length <= 0) return -1;
 return binarySearch(arr, 0, arr.length - 1, findVal);
 }
    public static int binarySearch(int[] arr, int left, int right, int findVal){
        if (left > right) return -1;
 int mid = (left + right) / 2;
 int midVal = arr[mid];
 if (findVal < midVal){
            return binarySearch(arr, left, mid - 1, findVal);
 }else if (findVal > midVal){
            return binarySearch(arr, mid + 1, right, findVal);
 }else {
            return mid;
 }
    }
    public static void main(String[] args) {
        int[] arr = {1,4,9,29,98,100,989};
 int result = binarySearch(arr, 10);
 System.out.println(result);
 }
}

插值查找

  • 介绍:插值查找是类似于折半查找,不同的地方在于mid的计算不同,折半查找是每次从取中间值,而插值查找每次从自适应mid处开始查找
  • 基本思路

    • 与折半查找相同,mid的计算方式不同
    • mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left])
  • 举个例子:比如有序数组{1,8,9,10,29,39,49,69},查找39

    • (Java实现)查找算法---线性、折半、插值、斐波那契如上图所示,经历了三次查找才找到了findVal
    • 这里用的例子由于数组里面的整数比较没有规律,所以经过了三次查找才找到,而折半查找只用了两次就找到了。但是如果是对于一组有规律的有序序列来说,查找的次数往往只需要一次(大家可以试一下从1-100里面去查找一个数)
  • 代码如下

    • 这里也是采用递归的方式实现,注意看一下下面的注释
public class InsertSearch {
    public static int insertSearch(int[] arr, int findVal){
        if (arr == null || arr.length <= 0) return -1;
 return insertSearch(arr, 0, arr.length - 1, findVal);
 }
    /**
 ** @param arr 有序数组
 * @param left 左边的下标
 * @param right 右边的下标
 * @param findVal 要找的值
 * @return findVal对应的下标,如果找不到则返回-1
 */ public static int insertSearch(int[] arr, int left, int right, int findVal){
        //以下三个判断条件必须写
 //第一个:当找不到的时候left会大于right
 //第二个和第三个:如果说要找的值不在该数组的最大值和最小值的范围里(对于升序的数组来说)
 // 那么如果不加这两个条件,可能会导致最后算出来的mid不在left和right之间
 if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) return -1;
 int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);
 int midVal = arr[mid];
 if (findVal > midVal){
            return insertSearch(arr, mid + 1, right, findVal);
 }else if (findVal < midVal){
            return insertSearch(arr, left, mid - 1, findVal);
 }else {
            return mid;
 }
    }
    public static void main(String[] args) {
        int[] arr = {1,8,9,10,29,39,49,69};
 System.out.println(Arrays.toString(arr));
 int result = insertSearch(arr, 39);
 System.out.println(result);
 }
}

斐波那契查找

  • 介绍:利用斐波那契数列找到数组中的黄金分割点

    • 黄金分割点:是指把一条线段分割为两部分,使其中一部分与全长之比等于另一部分与这部分之比。取其前三位数字的近似值是0.618。
    • 斐波那契数列中,两个相邻数的比例是无限接近与黄金分割值0.618
    • 斐波那契数列计算公式:F(k) = F(k-1) + F(k-2),F(1)=1,F(2)=2
  • 基本思路:

    • 该查找算法与折半查找类似,区别也是在于mid的计算,mid是取位于黄金分割点附近,即mid=low+F(k-1)-1
  • F(k-1)是什么?

    • 由F(k)计算公式的性质,可以得到F(k)-1 = (F(k-1) - 1) + (F(k-2) - 1) + 1
    • 也就是说,只要数组的长度为F(k)-1,就可以将该表分成长度为F(k-1) - 1 和F(k-2) - 1两段
    • 而中间位置mid = low + F(k - 1) - 1
  • 是否需要扩容数组:数组的长度n不一定等于F(k),所以需要将原来的数组长度n增加至F(k),不一定要完全相等,只要比F(k)大就行,所以当n增大后就需要进行扩容
  • K如何确定? 代码如下:
while(n > F(k))
    k++
  • 举个例子:比如有序数组{1,8,9,10,29,39,49,69},查找39

    • 1)首先判断是否需要扩容,n为8,k为5,F(5)=8,n刚好等于5,不需要扩容
    • 2)第一轮,根据公式计算出mid=4,arr[mid]=29,小于39,向右查找

      • left = mid + 1 = 5
      • right = 7
      • k = k - 2 = 3(为什么需要k-2,看下面的解释)
      • mid = 6(Java实现)查找算法---线性、折半、插值、斐波那契
    • 3)第二轮,arr[mid] = arr[6] = 49,大于39,向左查找

      • left = 5
      • right = mid - 1 =6
      • k = k - 1 =2(为什么需要k-1,看下面的解释)
      • mid = 5(Java实现)查找算法---线性、折半、插值、斐波那契
    • 4)第三轮,arr[mid] = arr[5] = 39,等于39,找到了(Java实现)查找算法---线性、折半、插值、斐波那契
  • k -= 2和 k --的含义

    • (Java实现)查找算法---线性、折半、插值、斐波那契如上图所示,f(k)-1可以分成两段,分别是f(k-1)-1和f(k-2)-1
    • 当要找的数比arr[mid]小,我们就把f(k)-1的长度缩减成f(k-1)-1,所以此时k--
    • 当要找的数比arr[mid]大,我们就把f(k)-1的长度缩减成f(k-2)-1,所以此时k -= 2
  • 代码如下
public class FibonacciSearch {
    private static int maxSize = 100;
 private static int count = 0;
 private static int[] fib(){
        int[] f = new int[maxSize];
 f[0] = 1;
 f[1] = 1;
 for (int i = 2; i <= maxSize - 1; i ++){
            f[i] = f[i - 1] + f[i - 2];
 }
        return f;
 }
    //斐波那契查找算法
 public static int fibSearch(int[] arr, int findVal){
        return fibSearch(arr, 0, arr.length - 1, findVal);
 }
    /**
 * * @param arr 有序数组
 * @param low 数组左边的下标
 * @param high 数组右边的下标
 * @param findVal 要查找的值
 * @return 找到的值对应的下标,如果没有返回-1
 */ public static int fibSearch(int[] arr, int low, int high, int findVal){
        if (arr == null || arr.length <= 0 ) return - 1;
 int k = 0; //表示斐波那契数列的下标
 int[] f = fib(); //获取斐波那契数列
 int mid = 0; //mid值
 //获取斐波那契分割数值
 while (high > f[k] - 1){
            k ++;
 }
        //把k与n进行比较,如果n小就需要进行扩容
 int[] temp = Arrays.copyOf(arr, f[k]);
 //把扩容多出来的数赋予原来arr的最后一个数据
 for (int i = high + 1; i < temp.length; i ++){
            temp[i] = arr[high];
 }
        System.out.println("查找之前的工作:");
 System.out.println("t扩容后的数组arr:" + Arrays.toString(temp));
 System.out.println("tleft:" + low);
 System.out.println("tright:" + high);
 System.out.println("tk:" + k);
 //使用while循环,找到我们的数Key
 while (low <= high){
            //设置中间点
 mid = low + f[k-1] - 1;
 System.out.println("这是第" + ++count +"轮:");
 System.out.println("tleft:" + low);
 System.out.println("tright:" + high);
 System.out.println("tk:" + k);
 System.out.println("tf[k-1]-1:" + (f[k-1] -1));
 System.out.println("tmid:" + mid);
 if (findVal < temp[mid]){
                high = mid - 1;
 //这里为什么是k --
 //1、全部元素 = 前面的元素 + 后边的元素
 //2、f(k) = f(k-1) + f(k-2)
 //3、前面有f(k-1)个元素,所以可以继续拆分,f(k-1) = f(k-2)+f(k-3)
 k --;
 }else if (findVal > temp[mid]){
                low = mid + 1;
 //为什么是k -= 2
 //1、全部元素 = 前面的元素 + 后面的元素
 //2、f(k) = f(k-1) + f(k-2)
 //3、因为我们有f[k-2],所以可以继续拆分f[k-2]=f[k-3]+f[k-4]
 //4、即在f[k-2]的前面进行查找
 //5、下次循环mid = f[k - 1 - 2] - 1
 k -= 2;
 }else {
                if (mid <= high){
                    return mid;
 }else {
                    return high;
 }
            }
        }
        return -1;
 }
    public static void main(String[] args) {
        int[] arr = {1,8,9,10,29,39,49,69};
 int resultIndex = fibSearch(arr, 39);
 System.out.println(resultIndex);
 }
}
点赞
收藏
评论区
推荐文章
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皮卡皮卡皮 皮卡皮卡皮
4年前
javaScript. Dom 基本操作
DOM节点查找jsdocument.getElementById()//通过id查找document.getElementsByTagName()//通过标签名document.getElementsByName()//通过name名查找document.getElementsByClassName("类名")//通过类名获取元素对象documen
林末 林末
4年前
折半查找-Python版(二分查找)
介绍二分查找也称折半查找(BinarySearch),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。前提必须待查找的序列有序时间复杂度O(log2n)原理1)确定该期间的中间位置K2)将查找的值t与arrayk比较,若相等,查找成功返回此位置;否则确定新的查找区域,继续二分
似梦清欢 似梦清欢
2年前
查找算法
顺序查找顺序查找又称为线性查找,对线性表和链表都适用。线性表可以通过数组下标递增来顺序扫描每个元素,链表可以通过next指针依次扫描每一个元素。:::tip指针实现顺序表时,顺序表中是指针时,在定义顺序表的结构体后,需要对顺序表初始化,初始化时为指针申请堆
Wesley13 Wesley13
3年前
java 二分查找算法
二分查找又称折半查找,它是一种效率较高的查找方法。将数列按有序(递增或递减)排列,查找过程中采用跳跃式方式查找,即先以有序数列的中点位置为比较对象,如果要找的元素值小于该中点元素,则将待查序列缩小为左半部分,否则为右半部分。通过一次比较,将查找区间缩小一半。它可以明显减少比较次数,提高查找效率。但是,表中的数据元素必
blmius blmius
4年前
linux find 命令查找文件和文件夹
查找目录:find/(查找范围)name'查找关键字'typed查找文件:find/(查找范围)name查找关键字print详解:find命令用来在指定目录下查找文件。任何位于参数之前的字符串都将被视为欲查找的目录名。如果使用该命令时,不设置任何参数,则find命令将在当前目录下查找子目录与文件。并且将查找到的子目录和文件全部进行显示。
九路 九路
4年前
7 二分搜索树的原理与Java源码实现
1折半查找法了解二叉查找树之前,先来看看折半查找法,也叫二分查找法在一个有序的整数数组中(假如是从小到大排序的),如果查找某个元素,返回元素的索引。如下:intarrnewint{1,3,4,6,8,9};在arr数组中查找6这个元素,查到返回对应的索引,没有找到就返回1思想很简单:1先找到数组中间元素ta
Wesley13 Wesley13
3年前
BFPRT线性查找算法
介绍:BFPRT算法解决的问题十分经典,即从某n个元素的序列中选出第k大(第k小)的元素,通过巧妙的分析,BFPRT可以保证在最坏情况下仍为线性时间复杂度。该算法的思想与快速排序思想相似,当然,为使得算法在最坏情况下,依然能达到o(n)的时间复杂度,五位算法作者做了精妙的处理。时间复杂度O(N)算法步骤
Wesley13 Wesley13
3年前
Java实现的二分查找算法
二分查找又称折半查找,它是一种效率较高的查找方法。折半查找的算法思想是将数列按有序化(递增或递减)排列,查找过程中采用跳跃式方式查找,即先以有序数列的中点位置为比较对象,如果要找的元素值小于该中点元素,则将待查序列缩小为左半部分,否则为右半部分。通过一次比较,将查找区间缩小一半。折半查找是一种高效的查找方法。它可以明显减少比较次数,提高查找效率。但是,
Stella981 Stella981
3年前
Kafka中改进的二分查找算法
最近有学习些Kafak的源码,想给大家分享下Kafak中改进的二分查找算法。二分查找,是每个程序员都应掌握的基础算法,而Kafka是如何改进二分查找来应用于自己的场景中,这很值得我们了解学习。由于Kafak把二分查找应用于索引查找的场景中,所以本文会先对Kafka的日志结构和索引进行简单的介绍。在Kafak中,消息以日志的形式保存,每个日志其实就是一个文
Wesley13 Wesley13
3年前
C#二分查找算法设计实现
C二分查找算法设计实现1.介绍二分查找也称折半查找(BinarySearch),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。(记住了前提要求是顺序存储结构,而且要有序排序,所以说对于一个无序的是没法用二分查找的)2.查找算法过程