[JDK17]斐波那契查找算法的实现原理、公式由来以及代码的实现(代码详解)

2024-03-26 04:50

本文主要是介绍[JDK17]斐波那契查找算法的实现原理、公式由来以及代码的实现(代码详解),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

JDK17完整代码实现:

package SearchAlgorithm.FibonaciiSearch;import java.util.Arrays;public class FibonaciiSearch {public static void main(String[] args) {int[] arr = {1,8,10,89,1000,1200};int targetIndex = fibonaciiSearch(arr,1200);System.out.println(targetIndex);}//斐波那契数列private static int[] fibonaciiList = getFibonaciiList(20);/*** 斐波那契查找算法(非递归算法)* 思路是利用数组长度,计算数组中的黄金分割点mid* 首先要清楚我们要把斐波那契数列中的每一个值当成数组的长度去看待,那么该数组长度的黄金分割点就是斐波那契数列的前一个值* @param arr* @param target* @return*/public static int fibonaciiSearch(int[] arr, int target) {//举例arr数组{1,8,10,89,1000,1200}int left = 0;int right = arr.length - 1;//k是指向斐波那契数列的索引,k所对应的斐波那契值(fibonaciiList[k])则是当前在left到right范围内数组的长度int k = 0;//找到该数组长度所位于斐波那契数列的位置索引k,再强调一下,斐波那契数列的每一个值我们都看成是数组的长度//现在要找的k,是当前数组长度在斐波那契数列中的位置索引while (arr.length > fibonaciiList[k]){k++;}//                          k//                          ↓//斐波那契数组:   {1,1,2,3,5,8,13,...},里面每一个值都看成是数组长度//斐波那契数组索引: [0,1,2,3,4,5, 6,...]//                          ↑//                          k//假设我们要找的数组是:{1,8,10,89,1000,1200},数组长度是6,那么该数组长度在斐波那契数列中对应的数组长度就应该是8//k是斐波那契数列的索引,所以k应该是5//如果当前k值对应的斐波那契值大于数组下标,则需要创建临时数组复制原数组并扩容至fibonaciiList[k]//再说一遍,斐波那契数组里面的每一个值都看成是数组的长度,那么,当前k所指向的斐波那契值是8,也就是要求要查找的数组arr需要有8个元素才符合对黄金分割点mid的计算int[] temp = Arrays.copyOf(arr, fibonaciiList[k]);//将填充的数据替换成arr的最后一个元素for (int i = arr.length; i < temp.length; i++) {temp[i] = arr[arr.length - 1];}//扩充并替换后的数组temp:{1,8,10,89,1000,1200,1200,1200}//mid是黄金分割点的索引int mid = 0;while (left <= right){//k == 0 说明当前查找的子序列只剩下一个元素。别忘了,k是斐波那契数列的下标,k==0说明f[k]==1,说明当前子序列长度为1if (k == 0){mid = left;}else {//斐波那契数组:   {1,1,2,3,5,8,13,...},里面每一个值都看成是数组长度//                          ↑//                          k//先摆公式      mid = left + f[k-1] -1//我们知道,黄金分割点mid的索引,其实是当前数组长度f[k],在斐波那契数列中位置的前一个斐波那契值f[k-1]//比如,当前数组长度是8,那么他的黄金分割比例就应该是5:3。再比如,如果当前数组长度是13,那么他的黄金分割比例应该是8:5//而黄金分割点mid,就是用来分割数组的,mid索引左右两边的子序列长度应该要满足黄金分割的比例//             left          mid  right//               ↓            ↓   ↓//数组temp:      {1,8,10,89,1000,1200,1200,1200}//temp数组索引:   [0,1, 2, 3,  4 , 5  ,  6 , 7  ]//                           ↑//                          mid//也就是说,我们应该要保证   mid的左子序列(包含mid)要有5个元素,  mid的右子序列(不包含mid)要有3个元素//所以黄金分割点按照上面的分析应该是mid = f[k-1] = 5,但是公式 mid = left + f[k-1] - 1 ,为什么最后还要再减1呢?//很显然,别忘记我们编程世界里的数组下标都是从0开始的,temp数组的索引是从0开始的,如果我们直接把f[k-1]当成mid的索引,显然不符合黄金分割比例的mid = left + fibonaciiList[k - 1] - 1;}if (target == temp[mid]){//如果temp[mid]就是目标值,而且当前mid不在扩充区就直接返回mid索引,否则返回right索引//                                 left//                                   ↓//比如数组temp:      {1,8,10,89,1000,1200,1200,1200} ,显然最后两个元素是扩充区,是原本arr数组没有的//                                   ↑    ↑//                                right  mid//这里说一下为什么mid会跑到left跟right的限定范围外?//因为当我们要找的目标值大于temp[mid]的时候,想要向右找子序列只会动left索引,right索引并不会去动它,计算出来的mid值是有可能超过left的,所以就可能会出现上面这种情况if (mid <= right) {return mid;}else {return right;}} else if (target < temp[mid]) {//目标值小于mid,向左找//这里解释为什么要k--?//                          k//                          ↓//斐波那契数组:   {1,1,2,3,5,8,13,...},里面每一个值都看成是数组长度//数组temp:      {1,8,10,89,1000,1200,1200,1200}//               ↑           ↑    ↑//             left         mid  right//因为当前数组长度是8,那么按照黄金分割比例,应该是mid左边(含mid)有五个元素,mid右边(不含mid)有3个元素//当我们想往左找目标值的时候,刚好k-1对应的斐波那契值就是5,而左边的子序列长度就是4,这时候就不需要扩容操作了,因为后面有元素,不会出现数组下标越界的情况//k--就是为了下一次循环做准备right = mid - 1;k--;} else if (target > temp[mid]) {//目标值大于mid,向右找//同往左找同理,往右找为什么需要k-2 ?//因为mid右边的子序列长度是3,刚好k-2对应的斐波那契值就是3,这样k=k-2就能保证我们下一次循环的k能够跟子序列数组长度3对应上left = mid + 1 ;k -= 2;}}//能出循环说明没找到目标值return -1;}/*** 获取斐波那契数列* @return*/public static int[] getFibonaciiList(int maxSize){int[] fibonaciiList = new int[maxSize];fibonaciiList[0] = 1;fibonaciiList[1] = 1;for (int i = 2; i < fibonaciiList.length; i++) {fibonaciiList[i] = fibonaciiList[i - 1] + fibonaciiList[i - 2];}return fibonaciiList;}
}

这篇关于[JDK17]斐波那契查找算法的实现原理、公式由来以及代码的实现(代码详解)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/847335

相关文章

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

详解C#如何提取PDF文档中的图片

《详解C#如何提取PDF文档中的图片》提取图片可以将这些图像资源进行单独保存,方便后续在不同的项目中使用,下面我们就来看看如何使用C#通过代码从PDF文档中提取图片吧... 当 PDF 文件中包含有价值的图片,如艺术画作、设计素材、报告图表等,提取图片可以将这些图像资源进行单独保存,方便后续在不同的项目中使