光头强说今天讲的是顺序,二分,插值,斐波那契查找算法(支持多元素查找)
🐻前言:
哎呀,学的太快,博主糊涂了,整理了一下发现查找算法居然没将,大意了,今天呢,咱们先放放堆排序,先看看咱们这个查找算法,查找算法呢有三种,有我们喜闻乐见的二分查找算法,哎呀,大家都学透了,但是呢,今天我重点介绍的是插值和斐波那契,给大家耳目一新.
我会从以下几点开始阐述:
查找算法的基础知识铺垫 🔜 查找算法的概述 🔜 查找算法的创建思路 🔜 查找算法的代码实现与分析 🔜 查找算法的总结
如果喜欢博主的话,戳这 传送门
精彩频道:
SQL的DBMS区 🔛 Java的数据结构区 🔛 SE的基础知识点区
往期精彩:
强哥带你去搬树
目录
🐻前言:
🦁查找算法的的基础知识铺垫:
😙斐波那契数列的定义:
🦁查找算法的概述:
🐼暴力查找:
🐼二分查找:
🐼插值查找:
🐼斐波那契查找:
🦁查找算法的创建思路
🐼二分查找算法(递归的方式):
🐼插值查找算法(递归的方式):
🐼斐波那契查找算法(循环的方式):
😶🌫️注意:
😀1.二分查找算法的代码实现与分析(普通版):
🥸代码分析:
😀2.二分查找法的代码实现和分析(升级版)
🥸代码分析:
😀3.插值查找算法(普通法):
🥸代码分析:
😀4.插值查找算法(升级版):
🥸代码分析:
😀5.斐波那契查找算法(普通版):
5.1建立一个斐波那契数列的方法🎆
🥸代码分析:
5.2创建斐波那契查找的方法 🎆
🥸代码分析:
🐻结论:
🦁查找算法的的基础知识铺垫:
😙斐波那契数列的定义:
如果在一个数列中,除了第一个元素和第二个元素是1,其他元素是前两个元素之和,我们称这样的数列为斐波那契数列
样例:[1,1,2,3,5,8,13.....]
🦁查找算法的概述:
🐼暴力查找:
暴力查找顾名思义,就是不用动脑子一个一个去遍历比较,用最朴素的代码完成最朴素的任务,因为这个算法太过简单了,这个代码就不写了也不分析了,相信大家都会的.
🐼二分查找:
二分查找是最熟悉的了,二分查找又被称为折半查找,通过左索引右索引与中间索引的动态变化从而大大提高查找的效率,接下来会是重点阐述对象之一,因为接下来的两种算法是以二分查找的思路为基础的.
🐼插值查找:
插值查找是对于初学者来说较为陌生的一个算法,不过,该算法写起来与二分查找算法极度相似,只是有个别小点不相同,注意的是,由于该算法太NB了,我也搞不懂里面的思想,只能教大家怎么去写,不过也不难写,放心哦
🐼斐波那契查找:
斐波那契查找算法顾名思义,里面用到了斐波那契数列,斐波那契查找算法也被称为黄金分割查找算法,故要找到黄金分割点去查找我们需要的元素,相对而言,斐波那契查找算法是查找算法里面最优的算法,故,我会着重讲述斐波那契查找算法的
🦁查找算法的创建思路
🐼二分查找算法(递归的方式):
- 创建一个查找算法,参数列表有left,right,findValue,array
- 创建一个变量mid,通过left和right获取mid
- 通过对mid的比较,决定递归方向
- 最后判断是否能找到
🐼插值查找算法(递归的方式):
- 创建一个查找算法,参数列表有left,right,findValue,array
- 创建一个变量mid,通过left和right获取mid
- 通过对mid的比较,决定递归方向
- 最后判断是否能找到
🐼斐波那契查找算法(循环的方式):
- 创建一个方法,用于构建斐波那契数列,并返回斐波那契数列
- 将带查找的数组长度与斐波那契数列里面的值去比较,找一个合适的值
- 若数组的个数不够,则在后面补最后一个结果,补刀数值相同为止
- 创建一个变量mid,通过left和right获取mid
- 通过特殊的转换关系继续循环(等等会具体阐述)
😶🌫️注意:
以上的算法都是建立在有序的基础上的,如果带查找序列是无序序列,不能用以上三种算法,只能暴力查找算法,谨记!
一下算法遵循此原则:如果找到了该元素,如果就会将该元素的下标返回回来,否则返回-1
如果找到了多个元素,就将该集合放回回来
😀1.二分查找算法的代码实现与分析(普通版):
public static int binarySearchSingle(int[] array,int left,int right,int findValue) { if(left > right || findValue > array[length-1]) { return -1; } var mid = left + 1/2*(right - left); var midValue = array[mid]; if(findValue > midValue) { return binarySearchSingle(array,mid+1,right,findValue); }else if(findValue < midValue) { return binarySearchSingle(array,left,mid-1,findValue); }else { return mid; } }
🥸代码分析:
- left是左索引,right是右索引,应该一开始就被赋值为0与array.length-1(因为带查找的值在他们两个索引之间,即[left,right]区间内有带查找元素)
- findValue是待查找的值,array是带查找的数组
- 二分查找法顾名思义是折半,所以mid = 1/2(left + right) = left + 1/2*(right - left)
- 注意:后面的表达式非常的重要,是折半查找发的关键
- 将mid下标对应的值放入midValue
- 情况:
- 如果midValue大于findValue,由于有序,说明待查找的元素在mid右边且不为mid,所以得出了[mid+1,right]的范围内有带查找元素,故left = mid+1
- 如果midVvalue小于findValue,由于有序,说明带查找的元素在mid左边且不为mid,所以得出了[left,mid-1]的范围内有带查找元素,故right = mid-1
- 如果midValue = findValue,说明找到了该元素,直接返回mid即可,mid是下标
- 如果left > right,由于没有这样的区间(即区间左边必须小于右边的值),说明找不到,直接返回-1
- 如果findValue大于列表的最后一个值,由于有序,最后一个值为最大值,说明列表里面不可能有与findValue相同的值,直接返回-1
😀2.二分查找法的代码实现和分析(升级版)
public static List binarySearchMore(int[] array,int left,int right,int findValue) { if(left > right || findValue > array[length-1]) { return new ArrayList(); } var mid = (left + right)/2; var midValue = array[mid]; if(findValue > midValue) { return binarySearchMore(array,mid+1,right,findValue); }else if(findValue < midValue) { return binarySearchMore(array,left,mid-1,findValue); }else { var list = new ArrayList(); list.add(mid); var temp = mid - 1; while(temp > 0 && array[temp] == findValue) { list.add(temp--); } temp = mid + 1; while (temp < array.length && array[temp] == findValue) { list.add(temp++); } return list; }
🥸代码分析:
- 注意:由于其他代码都一样,我们重点分析一下else里面的代码
- 当程序进入else时,说明找到了该元素,由于我们要实现多元素返回,我们可以借助集合,故我们要new一个ArrayLsit
- 先将该元素add进去集合中
- 创建一个辅助索引temp,赋值为mid-1(目的:向左遍历)
- 通过while循环,如果temp>0(防止数组下标越界异常),且array[temp] = findValue,说明此元素也是我们要找的元素,添加入集合中
- 如果while布尔表达式为false,说明temp遍历到了最左边或是不相等,由于列表的有序性,如果不相等,则前面的元素一定比midValue小,不可能再有带查找的元素,直接结束向左遍历
- temp赋值为mid+1(目的:向右遍历)
- 同理5~6
- 最后返回list即可
- 特别注意的是,如果找不到,我们返回一个空的集合,所以在最前面new了一个空集合
😀3.插值查找算法(普通法):
public static int interpolateSearch(int[] array,int left,int right,int findValue) { if(left > right || findValue array[array.length-1]) { return -1; } var mid = left + (right - left)*(findValue - array[left])/(array[right] - array[left]); var midValue = array[mid]; if(findValue > midValue) { return interpolateSearch(array,mid+1,right,findValue); }else if(findValue < midValue) { return interpolateSearch(array,left,mid-1,findValue); }else { return mid; } }
🥸代码分析:
- 插值查找算法唯一的不同就是mid的变化
- mid = left + (right-left)((findValue - array[left])/(array[right]-array[left]))
- 其他都一致,只需知道mid的赋值即可
😀4.插值查找算法(升级版):
public static List interpolateSearchMore(int[] array,int left,int right,int findValue) { if(left > right || findValue > array[array.length-1] || findValue < array[0]) { return new ArrayList(); } var mid = left + (findValue - array[left])/(array[right] - array[left]) * (right - left); var midValue = array[mid]; if(findValue > midValue ) { return interpolateSearchMore(array,mid+1,right,findValue); }else if(findValue < midValue ) { return interpolateSearchMore(array,left,mid-1,findValue); }else { var list = new ArrayList(); list.add(mid); // 短路与有学问呢 for (int temp = mid + 1;temp 0 && array[temp] == findValue ; temp++) list.add(temp); return list; } }
🥸代码分析:
略~~
😀5.斐波那契查找算法(普通版):
5.1建立一个斐波那契数列的方法🎆
public static int[] fibonacci() { var array = new int[20]; array[0] = 1; array[1] = 1; for (int i = 2; i < array.length; i++) { array[i] = array[i-1] + array[i-2]; } return array; }
🥸代码分析:
- 创建一个数组,用于存放斐波那契数列(长度可以随意)
- 将第一个和第二个元素赋值为1
- 建立循环,从第三个元素开始(即下标为2),当前元素的值是前两个元素之和
- 返回该数组
5.2创建斐波那契查找的方法 🎆
public static int fibonacciSearch(int[] array,int key) { var low = 0; var high = array.length-1; var mid = 0; var midValue = 0; var k = 0; var f = fibonacci(); while(high > f[k] - 1){ k++; } var temp = Arrays.copyOf(array,f[k]); for (int i = high+1; i < temp.length ; i++) { temp[i] = array[high]; } while(low midValue) { low = mid+1; k-=2; }else if(key < midValue) { high = mid-1; k-=1; }else { return Math.min(mid, high); } } return -1; }
🥸代码分析:
- low(左索引),right(右索引),mid(索引),midValue(索引对应的值),k(用于遍历斐波那契数组的索引),f(用于接收斐波那契数组)
- 注意:
- 斐波那契数组里面的值表示带查找数组的元素个数
- 第一步:我们要将斐波那契数组的索引k移动,移动至与数组元素相近的为止
- 有可能斐波那契数组里面的元素的值比数组的总长度大
- 有可能斐波那契数组里面的元素的值等于数组的总长度
- 注意:由于high是下标.比总长度少了1,所以在比较的时候在斐波那契数组k下标对应值上-1
- 第二步:我们要进行数组扩容的操作,拷贝后的长度是斐波那契数组中索引k对应的值
- 目的:因为数组长度严格符合斐波那契数组里面的元素值,才能找得到该数组的黄金分割点
- 无论是否是哪种情况,该做法是最安全的
- 第三步:利用循环在扩容的部分赋值原数组的最后一个元素,即temp[i] = array[high];
- 第四步:利用while循环去找所需求的值
- low<=high与上述一致,在这个区间内有我们要找的元素
- mid 赋值为f(k-1) -1
- 如果有f(k)个元素,那么黄金分割点前有f(k-1)个元素,黄金分割点后有f(k-2)个元素
- 因为有下标的因素,所以这里要-1
- 将黄金分割点对应下标的值赋予midValue
- 如果midValue < key,说明待寻找的值在黄金分割点的右边
- 故low = mid+1,表示在此区间有我们想要的元素
- 注意的是:此刻元素总数会从f(k)变成f(k-2),所以此刻要k-=2,让元素个数再次与斐波那契数组k对应元素一致,再次形成黄金分割点
- 如果midValue > key,说明带寻找的值在黄金分割点的左边
- 故high = mid -1,表示在此区间有我们想要的元素
- 注意的是:此刻元素总数从f(k)变成f(k-1),所以此刻要k-=1
- 如果midValue = key说明找到了
- 注意的是,有可能找到的元素下标比原数组最大下标大
- 故当我们发现找到的值比原数组最大下标大时,我们需要返回原数组最大下标
- 因为后面的元素都是用最后一个元素去补充的
- 循环结束后都找不到,说明真找不到了,直接返回-1
🐻结论:
查找算法说难不难,说易不易,主要是对二分查找法认识,自然而然的就会写插值查找发,不过最重要的还是斐波那契查找算法,他有效利用了斐波那契数组所体现出来的黄金分割思想,最后,我来总结一下几点:
1.二分查找法怎么去找,怎么去想
2.斐波那契查找怎么利用斐波那契数组,以及k的变化
🚇下一站:堆排序