一、个人感悟
1)线性查找
缺点非常明显,耗费的时间太长
优点是可以查找没有排好序的数组
2)二分查找
mid的算法是 left+(right - left)/2
缺点是需要排好序的数组
优点是对于线性查找大大减少耗费时间
3)插值查找
原理与二分查找类似,但是mid拥有了适应性
mid的算法变为 left+(right - left) * (findVal - arr[left]) / (arr[right] - arr[left])
这种算法非常适用于平均分布的数组比如{1,2,3,4—— 97,98,99,100}
缺点是需要排好序,如果分布跨度很大的话速度可能还不如二分查找
优点是如果分布均匀可以很快的找到目标数字
4)斐波那契查找
斐波那契数列:{1,1,2,3,5,8,13,21…}
与二分法不同的地方在于,运用了黄金分割比,
先建立一个斐波那契数列,然后mid每次的取值都是符合原数列的黄金分割比的,
在把原数列长度补到k指向的斐波那契数列里的值后,mid = left + f(k-1) -1,这样也就把数组分割为了左长右短两部分,如果比mid指向的值大的话,需要用到小的那一块数组,就把斐波那契数组中k往左移两个,如果要用到大的那一块,就左移一次
总结:
其实二分、插值、斐波那契的基本原理都是建立在二分之上的,差别只是mid的取值。二分比较万金油,插值更适合分布均匀,斐波那契我网上找了找是这样说的
与二分查找相比,斐波那契查找算法的明显优点在于它只涉及加法和减法运算,而不用除法。因此,斐波那契查找的平均性能要比折半查找好。
可能就像毕导套娃那期视频里说的,无论对计算机还是人来说加法永远比乘法好算吧
二、代码
1)线性查找
package 查找.线性查找;
public class SeqSearch {
public static void main(String[] args) {
int arr[] = {1,9,11,-1,34,89};
int index = seqSearch(arr,-1);
if (index == -1){
System.out.println("没找到");
}else {
System.out.println("下标为"+index);
}
}
public static int seqSearch(int[] arr, int value){
for (int i = 0; i < arr.length ; i++) {
if(arr[i] == value){
return i;
}
}
return -1;
}
}
2)二分查找
package 查找.二分查找;
import java.util.ArrayList;
import java.util.List;
public class BinarySearch {
public static void main(String[] args) {
// int[] arr = {1, 8, 10, 89, 1000, 1000, 1000, 1234};
int[] arr= {1,3,8,15,19,25,36,38,48,56,68,79,88,99,105,112,118,200};
// List<Integer> list = binarySearch2(arr, 0, arr.length - 1, 5);
int i = binarySearch(arr, 0, arr.length - 1, 1);
System.out.println(i);
// System.out.println(list);
}
public static int binarySearch(int[] arr, int left, int right, int findVal) {
System.out.println("被调用");
if (left > right) {
return -1;
}
int mid = (left + right) / 2;
if (arr[mid] < findVal) {
return binarySearch(arr, mid + 1, right, findVal);
} else if (arr[mid] > findVal) {
return binarySearch(arr, left, mid - 1, findVal);
} else {
return mid;
}
}
//找出每一个数字的下标
public static List<Integer> binarySearch2(int[] arr, int left, int right, int findVal) {
if (left > right) {
return null;
}
int mid = (left + right) / 2;
if (arr[mid] < findVal) {
return binarySearch2(arr, mid + 1, right, findVal);
} else if (arr[mid] > findVal) {
return binarySearch2(arr, left, mid - 1, findVal);
} else {
List<Integer> resIndexList = new ArrayList<>();
int temp = mid - 1;
while (temp >= 0 && arr[temp] == findVal) {
resIndexList.add(temp);
temp--;
}
resIndexList.add(mid);
temp = mid + 1;
while (temp <= arr.length - 1 && arr[temp] == findVal) {
resIndexList.add(temp);
temp++;
}
return resIndexList;
}
}
}
3)插值查找
package 查找.插值查找;
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[] arr= {1,3,8,15,19,25,36,38,48,56,68,79,88,99,105,112,118,200};
int i = insertValueSearch(arr,0,arr.length-1,1);
System.out.println(i);
// System.out.println(Arrays.toString(arr));
}
public static int insertValueSearch(int[] arr,int left, int right, int findVal){
System.out.println("被调用");
if (left > right || findVal < arr[0] || findVal > arr[arr.length-1]){
//为什么要比二分法多了两个比较?
//因为如果findVal如果非常大或者非常小在下面的程序中会导致mid很大或很小,就有可能就越界
return -1;
}
int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);
if (arr[mid] < findVal) {
return insertValueSearch(arr, mid + 1, right, findVal);
} else if (arr[mid] > findVal) {
return insertValueSearch(arr, left, mid - 1, findVal);
} else {
return mid;
}
}
}
4)斐波那契查找
package 查找.斐波那契;
import java.util.Arrays;
public class FibonacciSearch {
public static int maxSize = 20;
public static void main(String[] args) {
int[] arr= {1,3,8,15,19,25,36,38,48,56,68,79,88,99,105,112,118,200};
System.out.println(fibSearch(arr,3));
}
//非递归的获得一个斐波那契数列
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 fibSearch(int[] arr, int key){
int left = 0;
int right = arr.length-1;
int k = 0;//表示斐波那契数组的下标
int mid = 0;
int f[] = fib();
//获得斐波那契数列数值的下标
while (f[k] < right + 1){
k++;
}
//得到的k指向的数值可能比数值长度大,把数组长度补上
int[] temp = Arrays.copyOf(arr,f[k]);
//多出来的地方补上数组的最后一位数
for (int i = right +1; i < temp.length; i++) {
temp[i] = arr[right];
}
while (left <= right){
System.out.println("被调用");
mid = left + f[k-1] -1;//后一个减一是因为要的是数组下标,而f里的是长度
if (arr[mid] > key){
right = mid -1 ;
k--;//长度变成了分割成的两块里头较大的那一块
}else if (arr[mid] < key){
left = mid+1;
k-=2;//长度变为了分割成的两块里头教小的那一块
}else if (arr[mid] == key){
//有可能找到的是前面生成的那些数
if (mid > left){
return left;
}else {
return mid;
}
}
}
return -1;
}
}