Search Algorithm:Sequence Search, Binary Search, Interpolation Search, Fibonacci Search
线性查找(Sequence Search)
- 又称线性查找
- 时间复杂度为线性阶 O(n)
public class SequenceSearchApp {
/** 顺序查找(线性查找)
* 从下标0开始逐一对比, 找到了返回下标*/
public static int seqSearch(int arr[], int value) {
for (int i = 0; i < arr.length; i++) {
if(arr[i] == value) {
return i;
}
}
return -1;
}
public static void main(String[] args) {
int arr[] = {88, 77, 1, 66, -2, 100, 999};
System.out.println("下标为 " + seqSearch(arr, 77));
}
}
输出:
> 下标为 1
二分查找(Binary Search)
- 又称折半查找
- 原数据必须是有序的
- 查找公式& 过程:
- 公式: mid=(low+high)/2=low+1/2*(high-low)
- 原数据: [1, 2, 3, 4, 5]
- 首轮 2=(0+4)/2=0+0.5*4
- 第二轮 2=(1+3)/2=1+0.5*2 …
public class BinarySearchApp {
/** 二分查找(当有相同值元素时只会找出其中一个)
* @param arr 原始数组
* @param left 左下标
* @param right 右下标
* @param value 要查找的值*/
public static int binarySearch(int arr[], int left, int right, int value){
if (left > right) {
return -1;
}
int mid = (left + right) / 2;
int midValue = arr[mid];
if (midValue < value) { /** 锁定的中间值, 如果小于要查找的值时, 则向右递归进行查找*/
return binarySearch(arr, mid + 1, right, value);
} else if (midValue > value) { /** 锁定的中间值, 如果大于要查找的值时, 则向左递归进行查找*/
return binarySearch(arr, left, mid - 1, value);
} else {
/** 找到了, 则返回下标*/
return mid;
}
}
public static void main(String[] args) {
int arr[] = {-2, 1, 66, 77, 88, 100, 999};
System.out.println("下标为 " + binarySearch(arr, 0, arr.length - 1, 66));
}
}
输出:
> 下标为 2
/** 二分查找(将所有相同值的下标都返回*/
public static List<Integer> binarySearch2(int arr[], int left, int right, int value) {
if (left > right) {
return new ArrayList<>(0);
}
int mid = (left + right) / 2;
int midValue = arr[mid];
if (midValue < value) { /** 锁定的中间值, 如果小于要查找的值时, 则向右递归进行查找*/
return binarySearch2(arr, mid + 1, right, value);
} else if (midValue > value) { /** 锁定的中间值, 如果大于要查找的值时, 则向左递归进行查找*/
return binarySearch2(arr, left, mid - 1, value);
} else {
/** 当找到了, 不立即返回, 而继续查找*/
ArrayList<Integer> list = new ArrayList<>();
/** 从已找出的下标, 向左继续查找*/
int temp = mid - 1;
while (true) {
if (temp < 0 || arr[temp] != value) {
break;
}
list.add(temp);
temp -= 1;
}
list.add(mid);
/** 从已找出的下标, 向右继续查找*/
temp = mid + 1;
while (true) {
if (temp > arr.length -1 || arr[temp] != value) {
break;
}
list.add(temp);
temp += 1;
}
return list;
}
}
int arr[] = {-2, 1, 66, 77, 88, 88, 100, 999};
List<Integer> list = binarySearch2(arr, 0, arr.length - 1, 88);
System.out.println("下标为 " + list);
输出:
> 下标为 [4, 5]
二分查找(非递归实现)
- 时间复杂度为线性阶 O(log2n): 即查找目标最多只需 log2n步
public class BinarySearchApp {
public static void main(String[] args) {
int[] arr = {1, 3, 8, 10, 11, 67, 100};
int index = binarySearch(arr,100);
System.out.println("下标为 " + index);
}
/** 二分查找非递归实现*/
public static int binarySearch(int[] arr,int target) {
int left = 0;
int right = arr.length - 1;
while(left <= right) {
int mid = (right - left) / 2 + left;
if(arr[mid] == target) {
return mid;
} else if(arr[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
}
输出:
> 下标为 6
插值查找(Interpolation Search)
- 原数据必须是有序的
- 查找公式: mid=low+(high-low)*(key-arr[low])/(arr[high]-arr[low])
- 类似于二分查找, 但每次进行查找会自适应 mid
public class InterpolationSearchApp {
/** 插值查找
* @param arr 数组
* @param left 左边索引
* @param right 右边索引
* @param value 查找值*/
public static int interpolationSearch(int[] arr, int left, int right, int value) {
/** 为避免越界, 需判断 value < arr[0]和 value > arr[arr.length - 1]*/
if (left > right || value < arr[0] || value > arr[arr.length - 1]) {
return -1;
}
/** 算出自适应 mid*/
int mid = left + (right - left) * (value - arr[left]) / (arr[right] - arr[left]);
int midVal = arr[mid];
if (value > midVal) {
return interpolationSearch(arr, mid + 1, right, value);
} else if (value < midVal) {
return interpolationSearch(arr, left, mid - 1, value);
} else {
return mid;
}
}
public static void main(String[] args) {
int arr[] = {-2, 1, 66, 77, 88, 88, 100, 999};
System.out.println("下标为 " + interpolationSearch(arr, 0, arr.length - 1, 88));
}
}
输出:
> 下标为 4
斐波那契(黄金分割法)查找(Fibonacci Search)
- 概述: 斐波那契也是更改 mid来进行查找的, mid是通过斐波那契数列来获得, 即 mid=low+F(k-1)-1, 其中 F()是斐波那契数列
- 斐波那契数列{1,1,2,3,5,8,13,21,34,55…}, 这个数列规则为, 从第二项开始, 每一项都等于前两项之和, 越往后, 就越来越逼近黄金分割0.618, 因此又称为黄金分割数列
- 原数据必须是有序的
public class FibonacciSearchApp {
/** 制作斐波那契数列*/
public static int[] fibonacci() {
int length = 20;
int[] f = new int[length];
f[0] = 1;
f[1] = 1;
for (int i = 2; i < length; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f;
}
/** 通过斐波那契数列查找
* @param arr 数组
* @param value 查找值*/
public static int fibonacciSearch(int[] arr, int value) {
/** 左起始下标*/
int low = 0;
/** 右起始下标*/
int high = arr.length - 1;
/** 斐波那契数列的下标*/
int fibonacciKey = 0;
int mid = 0;
/** 获取斐波那契数列 [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]*/
int f[] = fibonacci();
/** 选取起始黄金分割点下标*/
while(high > f[fibonacciKey] - 1) {
fibonacciKey++;
}
/** 结果
* high 7
* fibonacciKey 5
* f[fibonacciKey] 8 - 1 = 7
* 7 > 7 = false*/
/** 如果起始黄金分割点(f[fibonacciKey] 8)大于原数组长度(high 7), 新生成 temp数组, 长度为黄金分割点 f[fibonacciKey]*/
int[] temp = Arrays.copyOf(arr, f[fibonacciKey]);
/** 结果: [-2, 1, 66, 77, 88, 9999, 0, 0] 不足的部分, 使用0填充*/
for(int i = high + 1; i < temp.length; i++) {
temp[i] = arr[high];
}
/** 结果: [-2, 1, 66, 77, 88, 9999, 9999, 9999] 将所有的填充值0改为, 原数组的最后元素值*/
while (low <= high) {
/**
* 第一轮: mid(4) = low(0) + f[fibonacciKey(5) - 1](5) - 1;
* - if (value(77) < temp[mid](88)) {
* - high(3) = mid(4) - 1;
* - fibonacciKey--;
* - }
* 第二轮: mid(2) = low(0) + f[fibonacciKey(4) - 1](3) - 1;
* - if (value(77) < temp[mid](66)) {
* - low(3) = mid(2) + 1;
* - fibonacciKey-= 2;
* - }
* 第三轮: mid(3) = low(3) + f[fibonacciKey(2) - 1](1) - 1;
* - if (mid(3) <= high(3)) return mid(3);*/
mid = low + f[fibonacciKey - 1] - 1;
if(value < temp[mid]) {
/** mid=f[k-1-1]-1
* 将原数组右下标向左移*/
high = mid - 1;
fibonacciKey--;
} else if (value > temp[mid]) {
/** mid=f[k-2-1]-1
* 将原数组左下标向右移*/
low = mid + 1;
fibonacciKey -= 2;
} else {
/** 找到, 将下标返回*/
if (mid <= high) {
return mid;
} else {
return high;
}
}
}
return -1;
}
public static void main(String[] args) {
int[] arr = {-2, 1, 66, 77, 88, 9999};
System.out.println("下标为 " + fibonacciSearch(arr, 77));
}
}
输出:
> 下标为 3
如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!