感觉老韩斐波那契查找法这里讲得不好,听得我一头雾水的,去找其他人视频讲解才勉强理解
package org.example._10查找;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Search {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 6, 6, 7, 8, 9};
//List<Integer> b_result = BinarySearch(arr, 0, arr.length - 1, 1);
//List<Integer> i_result = insertValueSearch(arr, 0, arr.length - 1, 5);
int f_result = fibSearch(arr, 9);
System.out.println(f_result);
}
//二分查找的前提是数组有序,返回的是要找的数的下标,注意要解决目标数重复的问题
public static List<Integer> BinarySearch(int[] arr, int left, int right, int findVal) {
System.out.println("HELLO_BINARY");
int mid = (left + right) / 2; //中点下标 mid = left + (right-left) * 1/2
int midVal = arr[mid]; //中点值
//这里不像插值查找需要findVal < arr[0] || findVal > arr[arr.length - 1]
//因为mid公式中不包含findVal
if (left > right) {
return new ArrayList<Integer>(); //没找到就返回一个空list
}
if (findVal < midVal) { //递归向左查找, 注意要return
return BinarySearch(arr, left, mid - 1, findVal);
} else if (findVal > midVal) { //递归向右查找
return BinarySearch(arr, mid + 1, right, findVal);
} else {
List<Integer> list = new ArrayList<>(); //list用来存储结果,因为要找的数可能重复出现
//对于一个二分查找序列,因为一定是有序的,所以目标值如果重复了就一定是连续的
//比如 1,2,3,3,3,4,5,我要找3,那么其中中间的那个3一定最后会是mid,这时就要扫描这个mid左右两边的情况
int temp = mid - 1; //扫描mid左边
while (true) {
//如果temp越界说明要找的数是数组第一个,左边第一个不是要找的数,都说明要找的数左边没有重复
if (temp < 0 || arr[temp] != findVal) break;
list.add(temp--); //如果有就放进list,然后指针左移
}
list.add(mid); //自然要把最终结果mid也放进list
temp = mid + 1; //然后扫描mid右边的情况
while (true) {
//同样的如果temp越界说明mid是最后一个数,如果arr[temp]找不到说明右边没有重复,都说明右边没有重复
if (temp > arr.length - 1 || arr[temp] != findVal) break;
list.add(temp++);
}
return list;
}
}
//插值查找.数据量比较大,元素值分布比较均匀跳跃性不大,和二分查找的区别就是换一下mid的公式
public static List<Integer> insertValueSearch(int[] arr, int left, int right, int findVal) {
System.out.println("HELLO_INSERT");
int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]); //插值点
int midVal = arr[mid]; //插值点值
//mid公式中包含findVal,导致mid的值可能很大,导致越界,所以加判断条件
if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) {
return new ArrayList<>(); //没找到就返回一个空list
}
if (findVal < midVal) { //递归向左查找
return insertValueSearch(arr, left, mid - 1, findVal);
} else if (findVal > midVal) { //递归向右查找
return insertValueSearch(arr, mid + 1, right, findVal);
} else {
List<Integer> list = new ArrayList<>(); //list用来存储结果,因为要找的数可能重复出现
//对于一个二分查找序列,因为一定是有序的,所以目标值如果重复了就一定是连续的
//比如 1,2,3,3,3,4,5,我要找3,那么其中中间的那个3一定最后会是mid,这时就要扫描这个mid左右两边的情况
int temp = mid - 1; //扫描mid左边
while (true) {
//如果temp越界说明要找的数是数组第一个,左边第一个不是要找的数,都说明要找的数左边没有重复
if (temp < 0 || arr[temp] != findVal) break;
list.add(temp--); //如果有就放进list,然后指针左移
}
list.add(mid); //自然要把最终结果mid也放进list
temp = mid + 1; //然后扫描mid右边的情况
while (true) {
//同样的如果temp越界说明mid是最后一个数,如果arr[temp]找不到说明右边没有重复,都说明右边没有重复
if (temp > arr.length - 1 || arr[temp] != findVal) break;
list.add(temp++);
}
return list;
}
}
//斐波那契查找法
//1.获取斐波那契数列
public static int[] fib() {
int[] f = new int[20]; //长度为20时斐波那契数列可以达到6765
f[0] = 1;
f[1] = 1;
for (int i = 2; i < f.length; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f;
}
//2.查找
public static int fibSearch(int[] arr, int findVal) {
int low = 0; //left
int high = arr.length - 1; //right
int k = 0; //mid的辅助值
int mid = 0; //用于查找的核心值
int f[] = fib();
//数组长度得先>=f[k]-1才能使用该方法查找,就是要找到最接近数组长度的那个斐波那契数
//这个数的值就是新数组的长度,这个数在斐波那契数列中的下标就是k
//比如n=10,那么最接近它的是13,13在斐波那契数列中下标是6,[1 1 2 3 5 8 13]
//所以new_length是13,而k=6
while (high > f[k] - 1) {
k++;
}
int[] temp = Arrays.copyOf(arr, f[k]);
//新数组中多出来的数得和原数组末尾数一样
for (int i = high + 1; i < temp.length; i++) {
temp[i] = arr[high];
}
//开始在新数组中找数
//为什么要搞一个新数组?因为比如当前新数组长度是13,因为是满足黄金分割比0.618的
//所以这个数组就可以分成前面8个和后面5个
//就可以按照斐波那契数列来进行分割和查找,以此类推
while (low <= high) {
mid = low + f[k - 1] - 1;//公式
//如果要找的数小于中间值,就往左找
if (findVal < temp[mid]) {
high = mid - 1;
k--; //前面是8个,那么因为[5,8,13],k就要从13这里跑到8这里,则k的下标减一
//如果要找的数大于中间值,就往右找
} else if (findVal > temp[mid]) {
low = mid + 1;
k -= 2; //跑到mid右边,k往前走两步
} else {
if (mid <= high) {
return mid; //如果mid和high相等那么mid就是对应值的下标
} else {
return arr.length - 1;//如果mid>high,那么说明要找的值是最后一个数,同时也是扩充的数
}
}
}
return -1;
}
}