重要的思想都在代码注释中
1.二分查找(折半查找)
适用条件:数组有序
时间复杂度:O(logn)
public int BinarySearch(int k, int[] arr) {
int minIndex = 0;
int maxIndex = arr.length - 1;
//(minIndex + maxIndex) / 2
int midIndex = minIndex + 1 / 2(maxIndex - minIndex);//可以稍微避免一下整形溢出
while(minIndex <= maxIndex){
if(k == arr[midIndex]){
return midIndex;
}else if(k > arr[midIndex]){
minIndex = midIndex + 1;
}else{
maxIndex = midIndex - 1;
}
midIndex = minIndex + 1 / 2(maxIndex - minIndex);
}
return -1;
}
2.插值查找(基于二分查找的扩展)
适用条件:数组有序且数值大致分布均匀
时间复杂度:平均情况O(loglog(n)),最坏O(log(n))
注:对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找的平均性能比折半查找要好。
package com.feng.demo;
//插值查找
public class InterpolationSearch {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7,8,9};
//要查找的数字
int k = 3;
int low = 0;
int high = arr.length - 1;
System.out.println(interpolationSearch(k,arr,low,high));
}
private static int interpolationSearch(int k, int[] arr, int low, int high) {
if(low > high){
return -1;
}
//这里就是插值查找的重点
//可以往相似三角形联想 ,(3 - 1)/(9 - 1)这是一个比例(所要查找的数字在数组中的大致位置与数
//组长度的比例),再乘以(8 - 0)的总长度,就是大致所要查找值的角标(不一定是,还要做具体的判
//断,但是比二分查找要好一些,因为二分查找总是对半分,比较死板,比如说我们查单词apple,翻英语
//词典的时候肯定是有目的往前面去找,所以我们的插值查找就用利用了这种思想。)
int mid = low + (int) 1.0 * (k - arr[low]) / (arr[high] - arr[low]) * (high - low);
if(mid < low || mid > high){
return -1;
}
if(arr[mid] == k){
return mid;
}else if(arr[mid] < k){
return interpolationSearch(k,arr,mid + 1,high);
}else{
return interpolationSearch(k,arr,low,mid - 1);
}
}
}
3.斐波那契查找(基于二分查找的扩展)
适用条件:数组有序
时间复杂度:O(logn)
虽然时间复杂度上看和二分查找一样,但是二分查找所用到的是乘除法,而斐波那契查找用到的是加减法,故能稍微快点。
package com.feng.demo;
import java.util.Arrays;
//斐波那契查找
public class FibonacciSearch {
public static int fibonacciSize = 20;
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7,8,9};
//查找2
System.out.println(fibSearch(arr,2));
}
private static int fibSearch(int[] arr, int key) {
int low = 0;
int high = arr.length - 1;
//根据黄金分割点的值从要查数组arr中找到存放黄金分割点的角标
int mid = 0;
//获取一个斐波那契数组(这个数组的作用就是用来找出一个可以用来进行黄金分割的数组长度值)
int[] fib = arrayOfFibonacci();
//从斐波那契数组中找到黄金分割点的值
int k = 0;
//用这个方法的前提:这个黄金分割点的值需要大于等于要查数组的长度
while(high + 1 > fib[k]){
k++;
}
//复制得到新的数组
int[] temp = Arrays.copyOf(arr,fib[k]);
//得到的新数组temp的长度可能大于要查数组arr的长度,
// 所以将多出来的位置都用要查数组arr的最后一个值来填充
for(int i = high + 1; i < temp.length - 1; i++){
temp[i] = arr[high];
}
//开始while循环,查找数字key
//被黄金分割后的两个数组我们默认左边长,右边短
// 假如说k = 4;fib(3) > fib(2);那么左边mid = low + fib[5 - 1] - 1;右边mid = low + fib[5 - 2] -1;
while(low <= high){
mid = low + fib[k - 1] - 1;
if(temp[mid] < key){
low = mid + 1;
//因为key是大于temp[mid]的,所以需要到右边长度短的数组中查找,所以k = k - 2;
//
k -= 2;
}else if(temp[mid] > key){
high = mid - 1;
k -= 1;
}else{
//这里是找到了,但这里可能会有特殊情况,因为我们先前有可能给新数组temp多出来的位置填充值了,但这些值的角标其实不存在要查数组arr的角标中,所以只需要返回high就行了
if(mid >= high){
return high;
}else{
//这里就是正常找到。
return mid;
}
}
}
return -1;
}
private static int[] arrayOfFibonacci() {
//数组实现斐波那契数列(比迭代能快一点,且好理解)
int[] fib = new int[fibonacciSize];
fib[0] = 1;
fib[1] = 1;
for(int i = 2; i < fibonacciSize; i++){
fib[i] = fib[i - 1] + fib[i - 2];
}
return fib;
//迭代实现斐波那契数列
// int[] fib = new int[fibonacciSize];
// int a = 1;
// int b = 1;
// fib[0] = a;
// fib[1] = b;
// int c = 0;
// for(int i = 2; i < fibonacciSize; i++){
// c = a + b;
// a = b;
// b = c;
// fib[i] = c;
// }
// return fib;
}
}