在java中 我们常用的查找有四种
①顺序(线性)查找
②二分查找/折半查找
③插值查找
④斐波那契查找
·线性查找算法
可以对有序或者无序的数组进行查找
代码:
//找到一个满足条件的就返回
public static int seqSearch(int []arr,int value){
for (int i = 0; i < arr.length; i++) {
if(arr[i]==value){
return i;
}
}
return -1;
}
·二分查找算法
只能对有序数组进行二分查找
·思路
①首先确定该数组的中间下标 mid=(left+right)/2
②然后让需要查找的数findValue和arr[mid]比较
·findValue>arr[mid] 说明你要查找的数在mid右边 因此需要递归的向右查找
·findValue<arr[mid] 说明你要查找的数在mid左边 因此需要递归的向左查找
·findValue=arr[mid] 说明找到了 返回
③找到了 就结束递归 若递归完整个数组仍然没找到findValue 也需要结束递归
即:left>right就需要退出
代码(递归法):
package search;
public class BinarySearch {
public static void main(String[] args) {
int []arr = {1,3,4,7,8,9,22};
int left = 0;
int right = arr.length-1;
int findValue=3;
int i = binarySearch(arr, left, right, findValue);
System.out.println(i);
}
private static int binarySearch(int[] arr, int left, int right, int findValue) {
int mid = (left + right) / 2;
int midValue = arr[mid];
if (left>right)
return -1;
if (findValue > midValue) {
return binarySearch(arr, mid + 1, right, findValue);
} else if (findValue < midValue) {
return binarySearch(arr, 0, mid - 1, findValue);
} else {
return mid;
}
}
}
思考:int [ ]arr = {1,3,4,7,7,7,22}; 怎么能把所有的7都找到
tips:找到mid的索引值 不要马上返回
①向mid索引值的左边扫描 将所有满足findValue(7)的元素的下标 加入到集合ArrayList
②向mid索引值的右边扫描 将所有满足findValue(7)的元素的下标 加入到集合ArrayList
③将ArrayList返回
package search;
import java.util.ArrayList;
public class BinarySearchs {
public static void main(String[] args) {
int []arr = {1,3,4,7,7,7,22};
int left = 0;
int right = arr.length-1;
int findValue=7;
ArrayList arrayList = binarySearch(arr, left, right, findValue);
System.out.println(arrayList.toString());
}
private static ArrayList<Integer> binarySearch(int[] arr, int left, int right, int findValue) {
int mid = (left + right) / 2;
int midValue = arr[mid];
if (left>right)
return new ArrayList<Integer>();
if (findValue > midValue) {
return binarySearch(arr, mid + 1, right, findValue);
} else if (findValue < midValue) {
return binarySearch(arr, 0, mid - 1, findValue);
} else {
ArrayList<Integer> integers = new ArrayList<>();
int temp = mid -1 ;
//左边
while (true){
if (temp<0||arr[temp]!=findValue){
break;
}
integers.add(temp);
temp--;
}
integers.add(mid);
//右边
temp = mid +1;
while (true){
if (temp>arr.length-1||arr[temp]!=findValue){
break;
}
integers.add(temp);
temp++;
}
return integers;
}
}
}
·插值查找算法
只能查找有序的数列
1)插值查找类似于二分查找 不同的是 插值查找每次从自适应mid处开始查找
2)将折半查找中的求mid索引的公式 low表示左边索引 high表示右边索引 key就是findValue
代码:
package search;
public class InsertValueSearch {
public static void main(String[] args) {
int []arr = new int[100];
for (int i = 0; i < 100; i++) {
arr[i]=i+1;
}
int i = insertValueSearch(arr, 0, arr.length - 1, 50);
System.out.println(i);
}
public static int insertValueSearch(int []arr,int left, int right,int findValue){
System.out.println("***");
if(left>right||findValue<arr[0]||findValue>arr[arr.length-1]){
return -1;
}
int mid = left + (right-left)*(findValue-arr[left])/(arr[right]-arr[left]);
if(arr[mid]>arr[right]){
return insertValueSearch(arr,mid+1,right,findValue);
}else if(arr[mid]<arr[left]){
return insertValueSearch(arr,left,mid-1,findValue);
}else {
return mid;
}
}
}
tips:
①对于数据量较大 且关键字分布比较均匀的查找表来说 采用 插值查找 速度较快
②关键字分布不均匀的情况下 该方法比一定比折半查找好
·斐波那契(黄金分割法)查找算法
只能查找有序数组
斐波那契数列 {1, 1, 2, 3, 5, 8, 13, 21, 34, 55 } 发现斐波那契数列的两个相邻数的比例,无限接近黄金分割值0.618
斐波那契(黄金分割法)原理:
斐波那契查找原理与前两种相似,仅仅 改变了中间结点(mid)的位置,mid不 再是中间或插值得到,而是位于黄金分割点附近,即mid=low+F(k-1)-1 (F代表斐波那契数列),如下图所示
对F(k-1)-1的理解: 由斐波那契数列 F[k]=F[k-1]+F[k-2] 的性质,可以得到 (F[k]-1)=(F[k-1]-1)+(F[k-2]-1)+1 。该式说明:只要顺序表的长度为F[k]-1,则可以将该表分成长度为F[k-1]-1和F[k-2]-1的两段,即如上图所示。从而中间位置为mid=low+F(k-1)-1
类似的,每一子段也可以用相同的方式分割 但顺序表长度n不一定刚好等于F[k]-1,所以需要将原来的顺序表长度n增加至F[k]-1。这里的k值只要能使得F[k]-1恰好大于或等于n即可,由以下代码得到,顺序表长度增加后,新增的位置(从n+1到F[k]-1位置),都赋为n位置的值即可。
斐波那契查找就是在二分查找的基础上根据斐波那契数列进行分割的。在斐波那契数列找一个等于略大于查找表中元素个数的数F[n],将原查找表扩展为长度为Fn,完成后进行斐波那契分割,即F[n]个元素分割为前半部分F[n-1]个元素,后半部分F[n-2]个元素,找出要查找的元素在那一部分并递归,直到找到。
值得一提的是当有序表的元素个数不是斐波那契数列中的某个数字时,需要把有序表的元素个数长度补齐,让它成为斐波那契数列中的一个数值,补上的数值是原数组的最后一个元素,当然把原有序表截断肯定是不可能的,不然还怎么查找。然后图中标识每次取斐波那契数列中的某个值时(F[k]),都会进行-1操作,这是因为有序表数组位序从0开始的,纯粹是为了迎合位序从0开始
如果这样你还是不明白,我只能举例给你来说明
比如有一个数组arr={1,2,3,4,5,6,7,8,9,10,11,12}要对他进行斐波那契查找,查找的值是10
首先,你得创建一个斐波那契数列出来我们定义为f[k],长度暂且定义为10吧那f={1,1,2,3,5,8,13,21,34,55},创建好了之后,我们再看原数组长度arr.length=12,根据斐波那契查找原则我们发现他的长度不等于斐波那契数列的某一个数值,所以我们要将数组的长度补至最近的斐波那契数,好,我们最近的值是13,所以我们复制最后一个元素至arr数组末尾(当然,数组长度是不能改变的,我们只能创建一个新的数组来复制arr数组的值并复制最后一个元素添加到末尾),好,新的数组元素就是{1,2,3,4,5,6,7,8,9,10,11,12,12}
接下来得找查找算法里的中间值了,斐波那契查找发就是讲原序列分为斐波那契数组里的连续的两个值,上面我们说了原序列长度为13,在斐波那契数列里找到f[k]=f[k-1]+f[k-2],即13=8+5,f[6]=f[5]+f[4],则中间值就是f[5]=8
找到中间值之后下来就是递归了,将目标值和中间值进行比较,如果目标值小于中间值,说明向左递归,将左边的部分继续分解为两个斐波那契数,以此类推直到找到目标数
斐波那契查找(黄金分割法)超详细详解_interesting_code的博客_斐波那契查找斐波那契查找思路说句实在话,这个斐波那契查找我看了不下5遍才理解他的思路和代码,因为它里面的值太多,不好理解容易绕晕,所以我给大家用自己的理解讲一下什么是斐波那契要想学会斐波那契查找,首先你得知道什么是斐波那契数列斐波那契数列,又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、····,在数学上,斐波那契被递归方法如下定义:F(1)=1,F(2)=1,F(n)=f(...
代码:
package search;
import java.util.Arrays;
public class FibonacciSearch {
public static int maxSize = 20;
public static void main(String[] args) {
int []arr= {1,8, 10, 89, 1000, 1234};
System.out.println(fibonacciSearch(arr, 1234));
}
//因为我们后面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;
}
public static int fibonacciSearch(int []a,int key){
int low = 0;
int high = a.length-1;
int k = 0;//表示斐波那契分隔数值的下标 即为mid=low+F(k-1)-1 中的k
int mid = 0;//存放mid值
int f[] = fib();//获取斐波那契数列
//获取斐波那契分割数值的下标
while (high>f[k]-1){
k++;
}
//因为f[k]值 可能大于a的长度 所以需要使用Arrays类 构造一个新数组 并指向a[]
//不足的部分会使用0填充
int []temp = Arrays.copyOf(a,f[k]);
//实际上需要使用a数组最后的数填充temp
for (int i = high+1; i < temp.length; i++) {
temp[i] = a[high];
}
/*
斐波那契数列{1,1,2,3,5,8}
eg:{1,8, 10, 89, 1000, 1234} high=6; 但是斐波那契数列里能包含6的最小值是8 所以
将数组扩充
{1,8, 10, 89, 1000, 1234}->{1,8, 10, 89, 1000, 1234,1234,1234}
*/
//找key
while (low<=high){
mid = low+ f[k-1]-1;
if(key<temp[mid]){//我们应该继续想数组左边查找
high = mid -1;
/*
这里k--是因为
①全部元素的个数=前面+后面元素的个数
②f[k] = f[k-1]+f[k-2]
因为前面有f[k-1]个元素 可以继续拆分 f[k-1] = f[k-2]+f[k-3]
即: 在f[k-1]的前面继续查找k--
即:下次循环 mid = f[k-1-1]-1;
*/
}else if ( key > temp[mid]) { // 我们应该继续向数组的后面查找(右边)
low = mid + 1;
//为什么是k -=2
//说明
//1. 全部元素 = 前面的元素 + 后边元素
//2. f[k] = f[k-1] + f[k-2]
//3. 因为后面我们有f[k-2] 所以可以继续拆分 f[k-1] = f[k-3] + f[k-4]
//4. 即在f[k-2] 的前面进行查找 k -=2
//5. 即下次循环 mid = f[k - 1 - 2] - 1
k -= 2;
} else { //找到
//需要确定一下返回的是哪个下标
if(mid <= high) {
return mid;
} else {
return high;
}
}
}
return -1;
}
}