数据结构与算法—查找算法(线性查找、二分查找、插值查找、斐波那契查找)

查找算法

文章目录

  • 查找算法
    • 1. 线性查找算法
    • 2. 二分查找算法
      • 2.1 二分查找思路分析
      • 2.2 应用实例
    • 3. 插值查找
      • 3.1 基本原理
      • 3.2 应用实例
    • 4. 斐波那契
      • 4.1 基本原理
      • 4.2 应用实例
    • 5. 查找总结

在java中,常用的查找有四种:

  1. 顺序(线性)查找
  2. 二分查找/折半查找
  3. 插值查找
  4. 斐波那契查找

1. 线性查找算法

线性查找太简单了,就是一个个遍历,看值对不对的上

韩老师代码如下

package com.atguigu.search;/*** @author 小小低头哥* @version 1.0* 线性查找*/
public class SeqSearch {public static void main(String[] args) {int arr[] = {1, 9, 11, -1, 34, 89}; //没有顺序的数组int index = seqSearch(arr, 11);if (index == -1) {System.out.println("没找到!");} else {System.out.println("找到了对应的位置index=" + index);}}/*** 实现线性查找 找到一个满足条件的值就返回* @param arr   数组* @param value 要查找的值* @return  找到就返回对应的索引值 没找到就返回-1*/public static int seqSearch(int[] arr, int value) {//线性查找是逐一对比 发现有相同值 就返回下标for (int i = 0; i < arr.length; i++) {if (arr[i] == value) {return i;}}return -1;  //没找到就返回-1}}

2. 二分查找算法

2.1 二分查找思路分析

在这里插入图片描述

图2 思路图

2.2 应用实例

受归并排序中递归的启发,觉得也可以用来查找。于是乎我自己用归并中递归写了一份查找代码

我写的这份代码中,包含单个查找、用于有序数组且返回所有相同值的索引、用于无序数组且返回所有相同值的索引

private static int count = 0;  //用来保存多次查找到数据位置的索引public static void main(String[] args) {int[] arr = {1, 8, 10, 10,10,10,10,10,11,89, 1000, 1234};int index;System.out.println("单个查找");index = divSearch(arr, 0, arr.length - 1, 10);if (index == -1) {System.out.println("没找到!");} else {System.out.println("找到了对应的位置index=" + index);}System.out.println("用于无序且返回所有相同值的索引");int[] arr2 = new int[arr.length];   //存放多个数值divSearch(arr, 0, arr.length - 1, 10, arr2);for (int i = 0; i < count; i++) {System.out.println("找到了对应的位置index=" + arr2[i]);}//重新置零count = 0;System.out.println("用于有序且返回所有相同值的索引");arr2 = new int[arr.length];   //存放多个数值divSearch2(arr, 0, arr.length - 1, 10, arr2);for (int i = 0; i < count; i++) {System.out.println("找到了对应的位置index=" + arr2[i]);}
}//用于有序且返回所有相同值的索引
public static int divSearch2(int[] arr, int left, int right, int value, int[] arr2) {if (left == right) {    //只剩下一个元素则退出if (arr[left] == value) {   //如果此元素为要查找的元素arr2[count++] = left;   //存起来}else if (count != 0) {   //说明上一个已经找到了 由于数组是有序的 那么这么如果不是相同的值的话//以后都不可能是了 直接退出了return 0;}return -1;  //返回-1表示没找到}int mid = (left + right) / 2;//左递归if (divSearch2(arr, left, mid, value, arr2) == 0) { //说明return 0;   //退出循环}//右递归if (divSearch2(arr, mid + 1, right, value, arr2) == 0) { //说明return 0;   //退出循环}return -1;  //否则继续循环
}//用于无序且返回所有相同值的索引
public static void divSearch(int[] arr, int left, int right, int value, int[] arr2) {if (left == right) {    //只剩下一个元素则退出if (arr[left] == value) {   //如果此元素为要查找的元素arr2[count++] = left;   //存起来}return;  //返回}int mid = (left + right) / 2;//左递归divSearch(arr, left, mid, value, arr2);//右递归divSearch(arr, mid + 1, right, value, arr2);
}public static int divSearch(int[] arr, int left, int right, int value) {if (left == right) {    //只剩下一个元素则退出if (arr[left] == value) {   //如果此元素为要查找的元素return left;}return -1;  //否则返回-1}int mid = (left + right) / 2;int index;index = divSearch(arr, left, mid, value);//左递归if (index != -1) {   //说明找到了对应的数return index;}//右递归index = divSearch(arr, mid + 1, right, value);if (index != -1) {   //说明找到了对应的数return index;}return -1;  //其他情况(不是只有一种元素的情况)均返回-1
}/*** @param arr     数组* @param left    左边的索引* @param right   右边的索引* @param findVal 要查找的值* @return 如果找到就返回下标 如果没有找到 就返回-1*/
public static int binarySearch(int[] arr, int left, int right, int findVal) {int mid = (left + right) / 2;int midVal = arr[mid];if ( left > right) {   //说明递归完了也没有找到对应的值 结束查找饿了return -1;}if (findVal > midVal) {return binarySearch(arr, mid + 1, right, findVal);  //右递归} else if (findVal < midVal) {return binarySearch(arr, left, mid - 1, findVal); //左递归} else {return mid;}}

韩老师代码如下,包含一个

/*** @param arr     数组* @param left    左边的索引* @param right   右边的索引* @param findVal 要查找的值* @return 如果找到就返回下标 如果没有找到 就返回-1*/
public static int binarySearch(int[] arr, int left, int right, int findVal) {int mid = (left + right) / 2;int midVal = arr[mid];if ( left > right) {   //说明递归完了也没有找到对应的值 结束查找饿了return -1;}if (findVal > midVal) {return binarySearch(arr, mid + 1, right, findVal);  //右递归} else if (findVal < midVal) {return binarySearch(arr, left, mid - 1, findVal); //左递归} else {return mid;}}/*** 1. 再找到mid索引值,不需马上返回* 2. 向mid索引值的左边扫描 将所有满足findVal的元素的下标 加入到集合ArrayList* 3. 向mid索引值的右边扫描 将所有满足findVal的元素的下标 加入到集合ArrayList* 4. 将ArrayList返回*/
public static ArrayList<Integer> binarySearch2(int[] arr, int left, int right, int findVal) {int mid = (left + right) / 2;int midVal = arr[mid];if ( left > right) {   //说明递归完了也没有找到对应的值 返回nullreturn null;}if (findVal > midVal) {return binarySearch2(arr, mid + 1, right, findVal);  //右递归} else if (findVal < midVal) {return binarySearch2(arr, left, mid - 1, findVal); //左递归} else {ArrayList<Integer> resIndexList = new ArrayList<>();//左扫描int temp = mid - 1;while (true){if(temp < 0 || arr[temp] != findVal){break;}//否则就放入到集合中resIndexList.add(temp--);}resIndexList.add(mid);//右扫描temp = mid + 1;while (true){if(temp > arr.length-1 || arr[temp] != findVal){break;}//否则就放入到集合中resIndexList.add(temp++);}return resIndexList;}
}

3. 插值查找

3.1 基本原理

  1. 查找算法类似于二分查找,不同的是插值查找每次从自适应mid处开始查找
  2. 将折半查找中的求mid索引的公式,low表示左边索引,high表示右边索引

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 ) (1) mid = low+\frac{key-a[low]}{a[high]-a[low]}(high-low)\tag{1} mid=low+a[high]a[low]keya[low](highlow)(1)

式中,key就是findVal(要查找的值),low就是arr[left] (左界限),high就是arr[right](右界限)

注意事项

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

3.2 应用实例

韩老师代码

public static int insertValueSearch(int[] arr, int left, int right, int findVal) {if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) {  //return -1;}//求出midint mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);int midVal = arr[mid];if (findVal > midVal) {return insertValueSearch(arr, mid + 1, right, findVal);  //右递归} else if (findVal < midVal) { return insertValueSearch(arr, left, mid - 1, findVal); //左递归} else {return mid;}
}

其实不难发现,和二分查找的区别就是mid的赋值方式变了。

4. 斐波那契

斐波那契数列 {1,1,2,3,5,8,13,21,34,55}中前后两相邻数的比例,无限接近黄金分割值0.618。

4.1 基本原理

  斐波那契查找原理于前两种相似,仅仅改变了中间节点(mid)的位置,mid不再是中间或插值得到,而是位于黄金分割点附近,即mid=low+F(k-1)-1(F代表斐波那契数列),如图所示。

在这里插入图片描述

图3 F(k)

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

  1. 由斐波那契数列F[K]=F[K-1]+F[K-2]的性质,可以得到(F[I]-1)=(F[k-1]-1)+(F[k-2]-1)+1。
  2. 类似的,每一子段也可以用相同的方式分割
  3. 但顺序表长度n不一定刚好等于F[K]-1,所以需要将原来的顺序表长度n增加至F[K]-1。这里的k值只要能使得F[K]-1恰好大于或等于n即可,由以下代码得到,顺序表长度增加后,新增的位置(从n+1到F[K]-1位置),都赋为n位置的值即可。

看原理有点难理解,直接看4.2代码更有感觉

while(n > fib(k)-1){k++;
}

4.2 应用实例

韩老师代码如下:并加入俺的注释!!!)

package com.atguigu.search;import java.util.Arrays;/*** @author 小小低头哥* @version 1.0* 斐波那契算法*/
public class FiBoNaQiSearch {private static int maxSize = 20;public static void main(String[] args) {int[] arr = {1, 2, 3, 4, 6, 7, 8, 9, 10};int i = fibSearch(arr, 10);System.out.println(i);}//因为后面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;}/*** 编写斐波那契查找算法** @param arr 数组* @param key 需要查找的关键值* @return 返回对应的下标 如果没有-1*/public static int fibSearch(int[] arr, int key) {int low = 0;//左索引起点int high = arr.length - 1;  //右索引终点//此处mid赋的值没意义 只是为了突出mid的意义 黄金分割点//从arr[low]到arr[mid]位置的长度设为m//从arr[mid]到arr[high]的长度假设为n//那么n/m的值应该逼近0.618//所以整个数组的长度应该是n+m-1(除去一个多算的arr[mid])//由于斐波那契数列f[]是一个前后相邻数相除逼近0.618的数组//所以可令n=f[k-2] m=f[k-1] 则n/m就逼近0.618  这就是为什么用到斐波那契数组的原因!!!//所以mid = low + m - 1 = low + f[k-1] - 1;//所以arr数组的长度应该为f[k-2]+f[k-1]-1=f[k]-1 !!!!//high - low + 1 整个数组的长度//0.618 * (high - low + 1) 黄金分割数组值//则从low开始的黄金分割长度为位置为 mid = low + 0.618 * (high - low + 1) - 1;int mid = (int) (low + 0.618 * (high - low + 1) - 1);    //黄金分割点int[] f = fib();    //得到斐波那契数组int k = 0;//所以由以上分析// arr的长度应该符合f[k]-1时//先把arr的长度扩容到对应的f[k]-1while (arr.length > f[k] - 1) {//说明arr的长度大于k位置的斐波那契数 则f[k] 不是所求的值k++;}//退出循环后 则说明找到了//开始扩容int[] temp = new int[f[k] - 1];for (int i = 0; i < temp.length; i++) {if (i < arr.length) {temp[i] = arr[i];} else {temp[i] = arr[high];    //扩容位置的数据都用arr最后一个位置的数据填充}}//接下来就是开始查找了while (low <= high) {    //当左索引大于右索引结束循环//由前面可知 从arr[low]到arr[mid]位置的长度为m//mid = low + m - 1 = low + f[k-1] - 1;mid = low + f[k - 1] - 1;//此后左边长度为f[k-1] 右边长度为f[k-2]if (key < temp[mid]) {  //说明需要向左继续判断//则下一个循环 整个数组查找范围应该为左边的low - mid-1//对应的长度为f[k-1] - 1high = mid - 1;//则由上面分析 下个循环中左边应该为f[k-1 - 1]k--;} else if (key > temp[mid]) {    //说明需要向右继续判断//则下一个循环 整个数组查找范围应该为右边的mid+1 - high//对应长度为f[k-2] - 1low = mid + 1;//则由上面分析 下个循环中左边应该为f[k-2 - 1]k -= 2;} else { //说明找到了if (mid >= arr.length - 1) {    //说明此时查找的数查到了扩容地方//那么实际应该是arr数组的最后一个元素的地方return arr.length - 1;} else {return mid;}}}return -1;  //没找到则返回-1}
}

5. 查找总结

  学完这几个查找算法后,其实感觉插值和斐波那契本质和二分查找算法思想上没啥差别,就是中点的选取有出入,二分查找规规矩矩为一半。值得注意的是,这几个查找法几乎都是针对有序数组。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

广州华锐互动:汽车电子线束加工VR仿真培训与实际生产场景相结合,提高培训效果

随着科技的不断发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术已经逐渐渗透到各个领域&#xff0c;为企业和个人带来了前所未有的便利。在汽车制造行业中&#xff0c;线束加工作为一项关键的生产工艺&#xff0c;其质量直接影响到汽车的性能和安全。因此&#xff0c;…

基于中小微企业_个体工商户的信贷评分卡模型和用户画像(论文_专利_银行调研建模使用)

背景介绍 信用贷款是指由银行或其他金融机构向中小微企业和个体工商户提供的一种贷款产品。该贷款的特点是无需提供抵押品或担保&#xff0c;主要依据借款人的信用状况来进行评估和审批。 中小微企业和个体工商户信用贷款的申请流程相对简单&#xff0c;申请人只需要提供个人…

MySQL数据库的基础概念

目录 顾名思义&#xff0c;数据库是用于存储数据的&#xff0c;那这些数据被存储在哪呢&#xff1f; 文件也能存储数据&#xff0c;那在这个基础上&#xff0c;为什么还要搞出一个数据库来存储数据呢&#xff1f; MySQL的客户端登录/退出指令、服务端的启动/关闭指令 数据…

如何查看PHP信息

创建一个 PHP 文件&#xff0c;比如 info.php&#xff0c;在其中添加以下代码&#xff1a; <?php phpinfo(); ?>访问这个文件&#xff08;例如&#xff0c;在浏览器中输入 http://localhost/info.php&#xff09;&#xff0c;它会显示 PHP 的所有配置信息。在这个页面…

服务器挖矿木马识别与清理

一、什么是挖矿木马 挖矿木马会占用CPU进行超频运算,从而占用主机大量的CPU资源,严重影响服务器上的其他应用的正常运行。黑客为了得到更多的算力资源,一般都会对全网进行无差别扫描,同时利用SSH爆破和漏洞利用等手段攻击主机。部分挖矿木马还具备蠕虫化的特点,在主机被成…

Threejs利用着色器编写动态飞线特效

一、导语 动态飞线特效是可视化数据地图中常见的需求之一&#xff0c;鼠标点击的区块作为终点&#xff0c;从其他区块飞线至点击区块&#xff0c;附带颜色变换或者结合粒子动画 二、分析 利用创建3点来构成贝塞尔曲线&#xff0c;形成线段利用着色器材质来按照线段以及时间…

Go语言学习:第1天

一、为什么开始学go语言 我自己是做测试的&#xff0c;所测试项目使用的是go语言。开始学习go语言的原因有两个&#xff1a;一方面&#xff0c;为了更好的做好工作&#xff1b; 另一方面&#xff0c;为了提高自己的核心竞争力。 二、第1天学习到的内容 2.1 Go是怎么解决包依…

ospf 知识总结

ospf 知识总结 一、ospf的概念 - 开放式最短路径优先协议&#xff0c;是广泛使用的一种动态路由协议&#xff0c;它属于链路状态路由协议&#xff0c;是一个内部网关协议&#xff08;IGP&#xff09;&#xff0c;用于在单一自治系统&#xff08;AS&#xff09;内决策路由。 - …

DeepMind大型语言AI模型FunSearch在数学科学中取得新发现

大型语言模型 (LLM) 是有用的助手 – 它们擅长组合概念&#xff0c;并且可以阅读、编写和编码来帮助人们解决问题。但他们能发现全新的知识吗&#xff1f;由于法学硕士已被证明会“幻觉”事实上不正确的信息&#xff0c;因此利用它们来做出可验证的正确发现是一个挑战。 FunSea…

HarmonyOS:使用MindSpore Lite引擎进行模型推理

场景介绍 MindSpore Lite 是一款 AI 引擎&#xff0c;它提供了面向不同硬件设备 AI 模型推理的功能&#xff0c;目前已经在图像分类、目标识别、人脸识别、文字识别等应用中广泛使用。 本文介绍使用 MindSpore Lite 推理引擎进行模型推理的通用开发流程。 基本概念 在进行开…

各技术栈需要掌握的知识

一、前端工程师需要掌握的知识 前端工程师需要掌握的知识主要包括以下几个方面: HTML、CSS和JavaScript:这是前端工程师的基础知识,需要熟练掌握。HTML是网页的骨架,CSS是网页的外观和样式,JavaScript则是实现网页交互效果的关键。响应式设计:随着移动设备的普及,响应…

测试用例设计方法之判定表详解!!

理论部分 判定表是分析和表达多种输入条件下系统执行不同动作的工具&#xff0c;它可以把复杂的逻辑关系和多种 条件组合的情况表达得既具体又明确。 条件桩(Condition Stub)动作桩(Action Stub&#xff09;条件项(Condition Entry&#xff09;动作项(Action Entry&#xff0…

Linux(4)-LAMP

L-LinuxA-apache/nginxM-mysqlp-php 搭建LAMP以及使用discuz搭建论坛网站 安装apache yum install httpd -y // 安装service httpd start // 启动Apache通过netstat -tunlp查看apache运行的端口&#xff0c;然后打开虚拟机ip 80端口能看到以下页面 或者 安装Mysql centOS6…

自然数分解 C语言xdoj64

输入说明 一个正整数 n&#xff0c;0<n<30 输出说明 输出n个连续奇数&#xff0c;数据之间用空格隔开&#xff0c;并换行 输入样例 4 输出样例 13 15 17 19 int main() {int n;scanf("%d",&n);if(n % 2 0){//n为偶数int in;//打印数字个数&#xff0c;做循…

【WINCC制作水管水流动画】

&#xff37;&#xff29;&#xff2e;&#xff23;&#xff23;简单制作水管水流动画 详情如下图所示&#xff1a; 1.首先用布化好管道&#xff0c;同时在管道内部画好折线图用以表示水流路径 2.选中折线图调整全局颜色方案 3.选择线条颜色 4.调整线条的线宽和线型 5.效果…

设计模式——组合模式(结构型)

引言 组合模式是一种结构型设计模式&#xff0c; 你可以使用它将对象组合成树状结构&#xff0c; 并且能像使用独立对象一样使用它们。 问题 如果应用的核心模型能用树状结构表示&#xff0c; 在应用中使用组合模式才有价值。 例如&#xff0c; 你有两类对象&#xff1a; ​…

关于Linux你必须知道的五件事

Linux是一种开源操作系统 (OS)。操作系统是直接管理系统硬件和资源&#xff08;如 CPU、内存和存储&#xff09;的软件。操作系统位于应用程序和硬件之间&#xff0c;并在所有软件和执行工作的物理资源之间建立连接。 俄罗斯军方计划用 Astra Linux 取代 Windows&#xff01;为…

JavaScript值类型和引用类型两道经典面试题

JavaScript值类型和引用类型两道经典面试题 题目1题目2 题目1 首先&#xff0c;小试牛刀&#xff0c;请看第一道题。 let a {x: 10 } let b a a.x 20 console.log(b.x)a {x: 30 } console.log(b.x) a.x 40 console.log(b.x);那么上述代码输出结果是多少呢&#xff1f; …

【Spring教程30】Spring框架实战:从零开始学习SpringMVC 之 Rest风格简介与RESTful入门案例

目录 1 REST简介2 RESTful入门案例2.1 环境准备2.2 思路分析2.3 修改RESTful风格 3 知识点总结 欢迎大家回到《Java教程之Spring30天快速入门》&#xff0c;本教程所有示例均基于Maven实现&#xff0c;如果您对Maven还很陌生&#xff0c;请移步本人的博文《如何在windows11下安…

Flink-水位线和时间语义

Flink中的时间含义 在实际应用中&#xff0c;事件时间语义会更为常见。一般情况下&#xff0c;业务日志数据中都会记录数据生成的时间戳&#xff08;timestamp&#xff09;&#xff0c;它就可以作为事件时间的判断基础。 在Flink中&#xff0c;由于处理时间比较简单&#xff0c…