[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

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

MyBatis常用XML语法详解

《MyBatis常用XML语法详解》文章介绍了MyBatis常用XML语法,包括结果映射、查询语句、插入语句、更新语句、删除语句、动态SQL标签以及ehcache.xml文件的使用,感兴趣的朋友跟随小... 目录1、定义结果映射2、查询语句3、插入语句4、更新语句5、删除语句6、动态 SQL 标签7、ehc

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关