二分查找
思想:二分查找必须是针对有序列表的,二分查找顾名思义,就是说每次查找都将一个数组对半分,拿到中间索引mid,然后通过判断需要查找的值(下面简称value)和mid索引在数组中的值(下面简称中间值)的大小关系,如果value大于中间值,也就是说value的位置将会在中间值的右边,那么就需要向右继续使用二分查找,循环往复最后就可以找到。可以使用递归或者迭代实现。
package com.search;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BinarySearch {
public static void main(String[] args) {
int[] arr = {0, 5, 11, 33, 213, 213, 213, 213, 213, 322, 456, 1231};
// List<Integer> searchIndex = binarySearch(arr, 0, arr.length - 1, 213);
// System.out.println(Arrays.toString(searchIndex.toArray()));
List<Integer> searchIndex2 = binarySearch2(arr, 213);
System.out.println(Arrays.toString(searchIndex2.toArray()));
}
/**
* 递归实现二分查找
*
* @param arr 有序数组
* @param left 左边界
* @param right 右边界
* @param value 需要找到的值
* @return 返回索引
*/
public static List<Integer> binarySearch(int[] arr, int left, int right, int value) {
if (left > right) {
return new ArrayList<>();
}
System.out.println("二分查询调用");
int mid = (left + right) / 2;
// 向右递归
if (arr[mid] < value) {
return binarySearch(arr, mid + 1, right, value);
} else if (arr[mid] > value) {
return binarySearch(arr, left, mid - 1, value);
} else {
// 当找到这个值的时候,分别向左向右遍历,因为是有序数组,相同的值是挨在一块的,因此使用遍历的方式更快。
List<Integer> resIndex = new ArrayList<>();
int tempIndex = mid - 1;
while (tempIndex >= 0 && arr[tempIndex]==value) {
resIndex.add(tempIndex);
tempIndex--;
}
resIndex.add(mid);
tempIndex = mid + 1;
while (tempIndex<= right && arr[tempIndex]==value) {
resIndex.add(tempIndex);
tempIndex++;
}
return resIndex;
}
}
/**
* 迭代查找
*
* @param arr 有序数组
* @param value 需要找到的值
* @return 返回索引
*/
public static List<Integer> binarySearch2(int[] arr, int value) {
if (arr[0] > value || arr[arr.length - 1] < value) {
return new ArrayList<>();
}
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
// 中间值比待查找值小,那么向右收缩
if (arr[mid] < value) {
left = mid + 1;
} else if (arr[mid] > value) {
right = mid - 1;
} else {
List<Integer> resIndex = new ArrayList<>();
int tempIndex = mid - 1;
while (tempIndex >= 0 && arr[tempIndex]==value) {
resIndex.add(tempIndex);
tempIndex--;
}
resIndex.add(mid);
tempIndex = mid + 1;
while (tempIndex<= right && arr[tempIndex]==value) {
resIndex.add(tempIndex);
tempIndex++;
}
return resIndex;
}
}
return new ArrayList<>();
}
}
插值查找
思想:通过value去动态的生成一个索引值,然后获取数组中对应这个索引值的值,通过类似于二分查找的比较去决定向哪一边查找
注意:插值查找同样是基于有序数组的,并且数组较大并且关键字分布均匀,采用插值查找速度较快;关键词分布不均匀的情况下,插值查找不一定有折半查找要好。
关键是生成这个索引值的方法,有一个公式:mid = low + (high−low) * ((key−array[low]) / array[high]−array[low])
也就是说key−array[low]先计算起始值到目标值的距离,然后除以起始值到结束值array[high]−array[low]的距离,就可以得到目标值在最小值到最大值中大概的位置,也就是一个比例,然后通过这个比例乘以起始位置到结束位置(high−low)的距离,就可以得到整个比例在整个距离上的大概位置了
也可以使用递归或者迭代实现
package com.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;
}
// int[] arr = {0, 5, 11, 33, 213, 213, 213, 213, 213, 322, 456, 1231};
int valueSearch = insertValueSearch2(arr, 0, arr.length - 1, 5);
System.out.println(valueSearch);
}
public static int insertValueSearch(int[] arr, int left, int right, int value) {
if (left > right || value < arr[left] || value > arr[right]) {
return -1;
}
System.out.println("插值查询调用");
// 动态求值的大概位置
int mid = left + (right - left) * ((value - arr[left]) / (arr[right] - arr[left]));
// 插值如果比要找的数小,那么说明需要往右边查找
if (arr[mid] < value) {
return insertValueSearch(arr, mid + 1, right, value);
} else if (arr[mid] > value) {
return insertValueSearch(arr, left, mid - 1, value);
} else {
return mid;
}
}
public static int insertValueSearch2(int[] arr, int left, int right, int value) {
if (left > right || value < arr[left] || value > arr[right]) {
return -1;
}
while (left <= right) {
// 动态求值的大概位置
int mid = left + (right - left) * ((value - arr[left]) / (arr[right] - arr[left]));
if (arr[mid] < value) {
left = mid + 1;
} else if (arr[mid] > value) {
right = mid - 1;
} else {
return mid;
}
}
return -1;
}
}