常见查找算法Java实现

  • 顺序(线性)查找
  • 二分查找/折半查找
  • 插值查找
  • 斐波那契查找

线性查找

判断数列是否包含要求,如果找到了,就提示找到了,并给出下标值

// 线性查找
public static ArrayList<Integer> seqSearch(int[] arr, int value) {
    ArrayList<Integer> list = new ArrayList<>();
    for (int i = 0; i < arr.length; i++) {
        if (value == arr[i]) {
            list.add(i);
        }
    }
    return list;
}

二分查找

需要对一个有序数组进行二分查找 {1, 8, 10, 89, 1000, 1234},输入一个数
看看该数组是否存在此数,并且求出下标,如果没有就提示”没有这个数”。

思路

  1. 确定该数组的中间的下标

    可以向下取整,也可以向上取整。e.g:4.5向上取整为 5;向下去整为4,此处演示为向下取整
    

    mid=(left+right)/2

  2. 让需要查找的数findVal和arr[mid]比较

    • findVal>arr[mid,说明你要查找的数在mid的右边,因此需要递归的向右查找
    • findVal<arr[mid],说明你要查找的数在mid的左边,因此需要递归的向左查找
    • findVal==arr[mid说明找到,就返回
  3. 递归结束

    • 找到就结束递归
    • 递归完整个数组,仍然没有找到findVal,也需要结束递归当Ieft>right就需要退出
/**
* 二分查找 ---要求数组为有序数组
* @param arr 需要查询的数组
* @param left 数组的最左侧下标
* @param right 数组的最右侧下标
* @param findVal 需要查询的值
* @return
*/
public static int binarySearch(int[] arr, int left, int right, int findVal) {
    // 当 left > right 时,整个数组遍历后并未找到值
    if (left > right) {
        return -1;
    }
    int mid = (left + right) / 2;
    int midVal = arr[mid];

    if (findVal > midVal) {// 右递归
        return binarySearch(arr, mid + 1, right, findVal);
    } else if (findVal < midVal) { // 向左递归
        return binarySearch(arr, left, mid - 1, findVal);
    } else {
        return mid;
    }
}

二分优化—多索引返回

// 二分查找 ---要求数组为有序数组
public static ArrayList<Integer> binarySearch(int[] arr, int left, int right, int findVal) {
    // 当 left > right 时,整个数组遍历后并未找到值
    if (left > right) {
        return new ArrayList<Integer>();
    }
    int mid = (left + right) / 2;
    int midVal = arr[mid];

    if (findVal > midVal) {// 右递归
        return binarySearch(arr, mid + 1, right, findVal);
    } else if (findVal < midVal) { // 向左递归
        return binarySearch(arr, left, mid - 1, findVal);
    } else {// 找到值
        ArrayList<Integer> resIndexList = new ArrayList<>();
        // 向 mid 的左右两边扫描
//            int temp = mid - 1;
//            while (true) {// 向左
//                if (temp < 0 || arr[temp] != findVal) {// 退出
//                    break;
//                }
//                // 否则,就temp 放入到resIndexList
//                resIndexList.add(temp);
//                temp -= 1; // temp左移
//            }
//            resIndexList.add(mid); // 将中间索引加入
//
//            temp = mid + 1;
//            while (true) {// 向右
//                if (temp > arr.length - 1 || arr[temp] != findVal) {// 退出
//                    break;
//                }
//                // 否则,就temp 放入到resIndexList
//                resIndexList.add(temp);
//                temp += 1; // temp右移
//            }

        // 再优化
        resIndexList.add(mid); // 将中间索引加入
        for (int i = mid - 1; i >= 0; i--) {// 向左
            if (arr[i] != findVal) {
                break;
            }
            resIndexList.add(i);
        }
        for (int i = mid + 1; i <= arr.length - 1; i++) {// 向右
            if (arr[i] != findVal) {
                break;
            }
            resIndexList.add(i);
        }

        return resIndexList;
    }
}

插值查找

需要数组是有序,效率比二分查找更高效

  • 插值查找算法类似于二分查找,不同的是插值查找每次从自适应mid处开始查找。

  • 将折半查找中的求mid索引的公式,low表示左边索引,high表示右边索引right

  • m i d = l o w + h i g h 2 = l o w + 1 2 ( h i g h − l o w ) mid=\frac{low+high}{2}=low+\frac{1}{2}(high-low) mid=2low+high=low+21(highlow) ➡️ m i d = l o w + k e y − a [ l o w ] a [ h i g h ] − a [ l o w ] ( h i g h − l o w ) mid=low+\frac{key-a[low]}{a[high]-a[low]}(high-low) mid=low+a[high]a[low]keya[low](highlow) 【其中key为需要查找的值】

  • 公式优化:int m i d I n d e x = l o w + ( h i g h − l o w ) ∗ ( k e y − a r r [ l o w ] ) / ( a r r [ h i g h ] − a r r [ l o w ] ) ; midIndex=low+(high-low)*(key-arr[low])/(arr[high]-arr[low]); midIndex=low+(highlow)(keyarr[low])/(arr[high]arr[low]);

// 插值查找 ---要求数组为有序数组
// 其中key为需要查找的值
public static int insertValueSearch(int[] arr, int left, int right, int key) {
    // 注意:key < arr[0] || key > arr[arr.length - 1]必须要有(优化作用),否则可能会造成midIndex越界
    if (left > right || key < arr[0] || key > arr[arr.length - 1]) {
        return -1;
    }

    int midIndex = left + (right - left) * (key - arr[left]) / (arr[right] - arr[left]);
    int midVal = arr[midIndex];
    if (key > midVal) {// 向右扫描递归
        return insertValueSearch(arr, midIndex + 1, right, key);
    } else if (key < midVal) {// 向左扫描递归
        return insertValueSearch(arr, left, midIndex - 1, key);
    } else {
        return midIndex;
    }

}
总结
  • 对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找,速度较快
  • 关键字分布不均匀的情况下,该方法不一定比折半查找要好

斐波那契查找(黄金分割法)

需要数组是有序

  • 黄金分割点是指把一条线段分割为两部分,使其中一部分与全长之比等于另一部分与这部分之比。取其前三位数字的近似值是0.618。由于按此比例设计的造型十分美丽,因此称为黄金分割,也称为中外比。这是一个神奇的数字,会带来意向不大的效果。
  • 斐波那契数列{1,1,2,3,5,8,13,21,34,55}发现斐波那契数列的两个相邻数的比例,无限接近黄金分割值0.618

原理

在这里插入图片描述

斐波那契查找原理与前两种相似,仅仅改变了中间结点(mid)的位置,mid不再是中间或插值得到,而是位于黄金分割点附近,即 m i d = l o w + F ( k − 1 ) − 1 mid=low+F(k-1)-1 mid=low+F(k1)1 (F代表斐波那契数列。k代表斐波那契个数)

对F(k-1)-1的理解:

  • 由斐波那契数列 F [ K ] = F [ k − 1 ] + F [ k − 2 ] F[K]=F[k-1]+F[k-2] F[K]=F[k1]+F[k2]的性质,可以得到 ( F [ k ] − 1 ) = ( F [ k − 1 ] − 1 ) + ( F [ k − 2 ] − 1 ) + 1 (F[k]-1)=(F[k-1]-1)+(F[k-2]-1)+1 (F[k]1)=(F[k1]1)+(F[k2]1)+1
    该式说明:只要顺序表的长度为 F [ k ] − 1 F[k]-1 F[k]1,则可以将该表分成长度为 F [ k − 1 ] − 1 F[k-1]-1 F[k1]1 F [ k − 2 ] − 1 F[k-2]-1 F[k2]1的两段,从而中间位置为 m i d = l o w + F ( k − 1 ) − 1 mid=low+F(k-1)-1 mid=low+F(k1)1

  • 类似的,每一子段也可以用相同的方式分割

  • 但顺序表长度 n n n不一定刚好等于 F [ K ] − 1 F[K]-1 F[K]1,所以需要将原来的顺序表长度n增加至 F [ K ] − 1 F[K]-1 F[K]1。这里的 k k k值只要能使得 F [ k ] − 1 F[k]-1 F[k]1恰好大于或等于 n n n即可,由以下代码得到,顺序表长度增加后,新增的位置(从 n + 1 n+1 n+1 F [ K ] − 1 F[K]-1 F[K]1位置),都赋为 n n n位置的值即可。

    while (high > f[k] - 1) {// 如果大于,则k++
        k++;
    }
    

注意:因为黄金分割点是第三段的前一段除以整段,之所以减1是因为数组是从0开始,所以 F [ k ] − 1 = ( F [ k − 1 ] − 1 ) + ( F [ k − 2 ] − 1 ) + 分割点 F[k]-1=(F[k-1]-1)+(F[k-2]-1)+分割点 F[k]1=(F[k1]1)+(F[k2]1)+分割点,第三点,由于f(k) 有可能小于n(也就是high),就不满足斐波那契数列要求

// 使用非递归方法得到一个斐波那契数列
public static int maxSize = 20;

// 后面需要mid=low+F(k-1)-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;
}

// 斐波那契查找 --- 非递归
// key为需要查找的关键码(值)
public static int fibSearch(int[] a, int key) {
    int low = 0;
    int high = a.length - 1;
    int k = 0; // 表示斐波那契分割数值的下标
    int mid = 0; // 存放a数组分割点
    int[] f = fib(); // 获取到斐波那契数列
    // 获取到斐波那契分割数值的下标
    // f[k] - 1 为临时数组的长度,不得小于a数组的长度
    while (high > f[k] - 1) {// 如果大于,则k++
        k++;
    }
    // f[k] 可能大于a 的长度,因此需要使用Arrays类构造一个新的数组,并指向temp[]
    // 不足的部分会使用0填充
    int[] temp = Arrays.copyOf(a, f[k]);
    // 实际上需要使用a数组的数填充temp
    // temp = {1, 8, 10, 80, 1000, 0, 0, 0} => {1, 8, 10, 80, 1000, 1000, 1000, 1000}
    for (int i = high + 1; i < temp.length; i++) {
        temp[i] = a[high];
    }

    // 使用while 来循环处理找到我们的数key
    while (low <= high) {
        mid = low + f[k - 1] - 1;
        if (key < temp[mid]) {// 向左边查找
            high = mid - 1;
            // f[k] = f[k-1] + f[k-2],之后f[k-1]继续拆分,因此k--
            k--;

        } else if (key > temp[mid]) { // 向右查找
            low = mid + 1;
            // f[k] = f[k-1] + f[k-2],之后f[k-2]继续拆分
            // f[k-1] = f[k-3] + f[k-4],因此k -=2
            k -= 2;
        } else {// 找到
            if (mid <= high) {
                return mid;
            } else {
                return high;
            }
        }
    }
    return -1;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/420407.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows 10 合并磁盘分区 (G and H)

Windows 10 合并磁盘分区 [G and H] 1. 设备和驱动器2. 计算机 -> 管理 -> 存储 -> 磁盘管理3. 删除卷4. 新建简单卷5. 设备和驱动器References 1. 设备和驱动器 2. 计算机 -> 管理 -> 存储 -> 磁盘管理 3. 删除卷 H: -> right-click -> 删除卷 H: 变…

项目案例:图像分类技术在直播电商中的应用与实践

一、引言 在数字化浪潮的推动下&#xff0c;电商行业迎来了一场革命性的变革。直播电商&#xff0c;作为一种新兴的购物模式&#xff0c;正以其独特的互动性和娱乐性&#xff0c;重塑着消费者的购物习惯。通过实时的直播展示&#xff0c;商品的细节得以清晰呈现&#xff0c;而互…

小红书关键词爬虫

标题 1 统计要收集的关键词&#xff0c;制作一个文件夹2 爬取每一页的内容3 爬取标题和内容4 如果内容可以被查看&#xff0c;爬取评论内容5 将结果进行汇总&#xff0c;并且每个帖子保存为一个json文件&#xff0c;具体内容6 总结 1 统计要收集的关键词&#xff0c;制作一个文…

Linux时间同步(PPS、PTP、chrony)分析笔记

1 PPS(pulse per second) 1.1 简介 LinuxPPS provides a programming interface (API) to define in the system several PPS sources. PPS means "pulse per second" and a PPS source is just a device which provides a high precision signal each second so t…

靠谱的车【华为OD机试-JAVAPythonC++JS】

题目描述 程序员小明打了一辆出租车去上班。出于职业敏感&#xff0c;他注意到这辆出租车的计费表有点问题&#xff0c;总是偏大。 出租车司机解释说他不喜欢数字4&#xff0c;所以改装了计费表&#xff0c;任何数字位置遇到数字4就直接跳过&#xff0c;其余功能都正常。 比如&…

外汇天眼:ASIC 获得针对前 Blockchain Global 董事的临时出行限制令

澳大利亚证券与投资委员会&#xff08;ASIC&#xff09;已经针对前Blockchain Global Limited&#xff08;清算中&#xff09;董事梁国&#xff08;又名Allan Guo&#xff09;获得了临时旅行限制令。这些命令在其他方面&#xff0c;阻止郭先生在2024年8月20日或进一步命令之前离…

C++数据结构与算法——二叉搜索树的属性

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…

C++数据结构与算法——二叉树的属性

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…

机器学习项目外包注意事项

将机器学习项目外包给外部团队或合作伙伴是一种常见的做法&#xff0c;特别是当您的团队缺乏特定领域的专业知识或资源时。以下是一些关于机器学习项目外包的要点和注意事项&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#…

【Unity】使用Unity实现双屏显示

引言 在使用Unity的时候&#xff0c;有时候会需要使用双屏显示 简单来说就是需要在两个显示器中显示游戏画面 双屏显示注意点&#xff1a; ①双屏显示需要电脑有两个显示 ②双屏显示只能用于PC端 ③不仅仅可以双屏&#xff0c;Unity最大支持8屏显示 1.相机设置 ①我们打开Un…

[VNCTF2024]-PWN:preinit解析(逆向花指令,绕过strcmp,函数修改,机器码)

查看保护&#xff1a; 查看ida&#xff1a; 这边其实看反汇编没啥大作用&#xff0c;需要自己动调。 但是前面的绕过strcmp还是要看一下的。 解题&#xff1a; 这里是用linux自带的产生随机数的文件urandom来产生一个随机密码&#xff0c;然后让我们输入密码&#xff0c;用st…

【论文笔记】An Effective Adversarial Attack on Person Re-Identification ...

原文标题&#xff08;文章标题处有字数限制&#xff09;&#xff1a; 《An Effective Adversarial Attack on Person Re-Identification in Video Surveillance via Dispersion Reduction》 Abstract 通过减少神经网络内部特征图的分散性攻击reid模型。 erbloo/Dispersion_r…

【C语言】常见的动态内存管理错误

前言 上一篇介绍了C语言中 动态内存管理函数&#xff0c;本片讲解的是 在我们使用动态内存管理时 常见的错误&#xff0c;一起来看看吧~ 欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 1.对NULL指针的解引⽤操作 错…

深入解析Golang的encoding/ascii85库:从基础到实战

深入解析Golang的encoding/ascii85库&#xff1a;从基础到实战 引言基础知识什么是ASCII85编码&#xff1f;ASCII85编码的工作原理ASCII85编码的优点ASCII85编码的缺点 使用Golang的encoding/ascii85库引入encoding/ascii85包ASCII85编码ASCII85解码实战示例小结 进阶技巧和最佳…

Vue3(pinia) 整合 SpringWebsocket链接url动态传参

前言&#xff1a; &#x1f44f;作者简介&#xff1a;我是笑霸final&#xff0c;一名热爱技术的在校学生。 &#x1f4dd;个人主页&#xff1a;个人主页1 || 笑霸final的主页2 &#x1f4d5;系列专栏&#xff1a;java专栏 &#x1f4e7;如果文章知识点有错误的地方&#xff0c;…

轧辊品质检测 直线度测量仪满足多种数据监测!

轧辊有带钢轧辊、型钢轧辊、线材轧辊、开坯辊、粗轧辊、精轧辊、破鳞辊、穿孔辊、平整辊、钢轧辊、铸铁轧辊、硬质合金轧辊、陶瓷轧辊等&#xff0c;但不管哪种类型的轧辊&#xff0c;对直线度测量都可以通过直线度测量仪来实现&#xff0c;这种测量仪检测方便&#xff0c;数据…

现货黄金贵金属投资难不难做?

现货黄金投资的难度因人而异&#xff0c;它涉及市场知识、分析能力、资金管理和心理素质等多个方面&#xff0c;因此不能一概而论。但是&#xff0c;如果投资者能够系统地学习相关知识&#xff0c;并在实践中不断积累经验&#xff0c;那么现货黄金投资并非难以驾驭。 先了解现货…

《汇编语言》- 读书笔记 - 第13章-int 指令

《汇编语言》- 读书笔记 - 第13章-int 指令 13.1 int 指令13.2 编写供应用程序调用的中断例程中断例程&#xff1a;求一 word 型数据的平方主程序中断处理程序执行效果 中断例程&#xff1a;将一个全是字母&#xff0c;以0结尾的字符串&#xff0c;转化为大写主程序中断处理程序…

作业1-224——P1927 防护伞

思路 遍历一下找到两点间的最远距离&#xff0c;直接公式算结果&#xff0c;控制输出位数 参考代码 #include<iostream> #include<iomanip> #include<cmath> using namespace std; int main() { int n; cin>>n; int x[n],y[n]; do…

hive报错:FAILED: NullPointerException null

发现问题 起因是我虚拟机的hive不管执行什么命令都报空指针异常的错误 我也在网上找了很多相关问题的资料&#xff0c;发现都不是我这个问题的解决方法&#xff0c;后来在hive官网上与hive 3.1.3版本相匹配的hadoop版本是3.x的版本&#xff0c;而我的hadoop版本还是2.7.2的版本…