常用查找
顺序查找
private static ArrayList<Integer> temp = new ArrayList<>();
public static void sequenceSearch(int[] arr, int value) {
if (!temp.isEmpty()) temp.clear();
for (int i = 0; i < arr.length; i++) {
if (arr[i] == value) temp.add(i);
}
if (!temp.isEmpty()) System.out.printf("值%d的索引为%s", value, temp);
else System.out.println("empty");
}
二分查找
该数组应为有序数组
思路
/**
* 先找中轴,然后target和中轴比较,看target在哪个区间,逐步缩小范围
* @param arr 带查找数组
* @param left 数组左边索引
* @param right 数组右边索引
* @param target 查找的值
*/
public static List<Integer> binarySearch(int[] arr, int left, int right, int target) {
int mid = (left + right) / 2; // 中轴索引
int midVal = arr[mid]; // 中轴值
if (left > right) return new ArrayList<>(); // 找不到的情况,返回空的集合
// 寻找过程,向右和向左递归
if (target > midVal) return binarySearch(arr, mid + 1, right, target);
else if (target < midVal) return binarySearch(arr, left, mid - 1, target);
else { // 找到时,检查是否有相同元素
// 向左循环检查是否有相同元素
int temp = mid - 1;
while (temp >= 0 && arr[temp] == target) res.add(temp--);
res.add(mid);
// 向右循环检查是否有相同元素
temp = mid + 1;
while (temp <= arr.length - 1 && arr[temp] == target) res.add(temp++);
return res;
}
}
利用java8特性,ArrayList转int[]
int[] intArr = res.stream().mapToInt(Integer::intValue).toArray();
非递归实现
/**
* 二分查找的非递归实现
* @author Bill Ludwig; 2020/6/24 11:48
*/
public class BinarySearch {
public static void main(String[] args) {
int[] arr = {1, 3, 8, 10, 11, 67, 100};
System.out.println(binarySearch(arr, 100));
}
/**
* 非递归地实现二分查找一个升序排列
* @param nums 数组
* @param target 目标
* @return 返回target在数组里的索引
*/
private static int binarySearch(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (target > nums[mid]) left = mid + 1;
else if (target < nums[mid]) right = mid;
else return mid;
}
return -1;
}
}
插值查找
原理
其中key为target, a为arr
public static List<Integer> insertionSearch(int[] arr, int left, int right, int target) {
System.out.println("called"); // 验证优化后的索引
// 以下判断防止索引越界
if (left > right || target < arr[0] || target > arr[arr.length - 1]) return new ArrayList<>();
int mid = left + (right - left) * (target - arr[left]) / (arr[right] - arr[left]); // 优化索引
int midVal = arr[mid];
if (target > midVal) return insertionSearch(arr, mid + 1, right, target);
else if (target < midVal) return insertionSearch(arr, left, mid - 1, target);
else { // 找到时,检查是否有相同元素
// 向左循环检查是否有相同元素
int temp = mid - 1;
while (temp >= 0 && arr[temp] == target) res.add(temp--);
res.add(mid);
// 向右循环检查是否有相同元素
temp = mid + 1;
while (temp <= arr.length - 1 && arr[temp] == target) res.add(temp++);
return res;
}
}
应用
斐波那契查找
概念
原理
mid就是黄金分割点
private static List<Integer> res = new ArrayList<>();
/**
* fib[k]-1是黄金分割段,具体看笔记
* @param arr 数组
* @param target 目标
* @return 索引
*/
public static List<Integer> fibSearch(int[] arr, int target) {
int low = 0;
int high = arr.length - 1;
int mid;
int k = 0;
int[] fib = fibArr;
// 获取k,k是fib的索引,在fib[k]-1大于high之前,k一直自增,可能会大于high
// 其实就相当于high + 1 > fib[k] ==》 为了使fib[k] >= arr.length,k++
while (high > fib[k] - 1) k++;
// 新建temp是为了配合斐波那契数列及它的公式
// 因为fib[k]可能会大于arr.length,即high + 1 < fib[k]。
// 所以新建一个指向arr的数组,长度为fib[k],temp不足的由0补充
int[] temp = Arrays.copyOf(arr, fib[k]);
// 然而实际上不能用0填充,否则无法比较;要用arr最后一个元素填充temp的空位!
for (int i = high + 1; i < temp.length; i++) temp[i] = arr[high];
// 查找target。k的变化具体看笔记。
// target小于mid的话,就是逐层拆分并循环拆分的前半部分,所以k自减1;同理,大于则自减2
while (low <= high) {
mid = low + fib[k - 1] - 1; // 黄金分割点
if (target < temp[mid]) {
high = mid - 1;
k--;
} else if (target > temp[mid]) {
low = mid + 1;
k -= 2;
}
// 找到target。mid有可能大于high,
// 因为temp[high]后面的值是用arr[high]填充的,
// 值都一样,但索引不一样, 所以需要返回较小的索引
else {
int index = mid - 1;
if (mid <= high) {
while (index >= 0 && temp[index] == target) res.add(index--);
res.add(mid);
index = mid + 1;
while (index <= arr.length - 1 && temp[index] == target) res.add(index++);
} else {
index = high - 1; // 这里就不需要查右边了,毕竟右边都是填充的值,假的
while (index >= 0 && temp[index] == target) res.add(index--);
res.add(high);
}
return res;
}
}
return new ArrayList<>(); // 没有找到
}
// 生成斐波那契数列
private static int[] fibArr = new int[21];
static {
fibArr[0] = 1;
}
private static int fib(int n) {
if (n < 0 || n > 92) return 0; // 基本结束条件
if (fibArr[n] == 0) fibArr[n] = fib(n - 1) + fib(n - 2);
return fibArr[n];
}