查找算法
1. 线性查找
线性查找(Sequential Search):从头开始逐个查找。
代码
import java.util.ArrayList;
public class SequentialSearch {
public static void main(String[] args) {
int[] arr = {1, 8, 4, 5, 3, 8, 2, 6, 7, 8, 9, 8};
System.out.println("—————————————线性查找——————————————");
int value = 8;
ArrayList index = sequentialSearch(arr, value);
if (index.isEmpty()) {
System.out.println("arr中没有" + value);
} else {
for (int i = 0; i < index.size(); i++) {
System.out.println("arr[" + index.get(i) + "] = " + value);
}
}
}
// 线性查找
public static ArrayList<Integer> sequentialSearch(int [] arr, int value) {
ArrayList<Integer> arrayList = new ArrayList<>();
for (int i = 0; i < arr.length; i++) {
if (arr[i] == value) {
arrayList.add(i);
}
}
return arrayList;
}
}
/* Code Running Result
—————————————线性查找——————————————
arr[1] = 8
arr[5] = 8
arr[9] = 8
arr[11] = 8
*/
2. 二分查找(折半查找)
二分查找(Binary Search)是在有序序列中查找value,取区间的中间的数值和查找的数值(value)比较,如果value小于中间数值就从左边的区间继续二分查找,如果value大于中间数值就从右边的区间继续二分查找,如果相等则就找到了value数值。
import java.util.ArrayList;
public class BinarySearch {
public static void main(String[] args) {
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 9};
System.out.println("—————————————二分查找——————————————");
int value = 8;
ArrayList<Integer> index = binarySearch(arr, 0, arr.length - 1, value);
if (index.isEmpty()) {
System.out.println("arr中没有" + value);
} else {
for (int i = 0; i < index.size(); i++) {
System.out.println("arr[" + index.get(i) + "] = " + value);
}
}
}
/** 二分查找
* @description: 如果找到返回 value值的下标就都放在 ArrayList 里面
* @Param [arr, left, right, value]: 从 arr (有序)数组中[left, right]区间中查找 value
* @return: 返回一个 ArrayList 对象
*/
public static ArrayList<Integer> binarySearch(int [] arr, int left, int right, int value) {
if (left > right) {
return new ArrayList<Integer>();
}
int mid = (left +right) / 2;
if (value < arr[mid]) { // 向左递归
return binarySearch(arr, left, mid - 1, value);
} else if (value > arr[mid]) { // 向右递归
return binarySearch(arr, mid + 1, right, value);
} else { // 找到
ArrayList<Integer> arrayList = new ArrayList();
// 把左边与 value 值相等的元素的下标添加到 arrayList 中
for (int i = mid - 1; i >= 0 && arr[i] == value; i--) {
arrayList.add(i);
}
// 把 mid 添加到 arrayList 中
arrayList.add(mid);
// 把右边与 value 值相等的元素的下标添加到 arrayList 中
for (int i = mid + 1; i < arr.length && arr[i] == value; i++) {
arrayList.add(i);
}
return arrayList;
}
}
}
/* Code Running Result
—————————————二分查找——————————————
arr[8] = 8
arr[7] = 8
arr[9] = 8
arr[10] = 8
*/
3. 插值查找
插值查找(Interpolation Search)是在有序序列中查找 value,插值查找算法类似于二分查找,不同的是插值查找每次从自适应 mid 处开始查找。
二分查找:mid = ( left + right ) / 2
即 mid = left + ( right - left) * 1 / 2
插值查找:mid = left + ( right - left ) * ( value - arr[left] ) / ( arr[right] - arr[left] )
插值查找注意
- 对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找,速度较快。
- 关键字分布不均匀的情况下,该方法不一定比二分查找好。
代码
import java.util.ArrayList;
public class InterpolationSearch {
public static void main(String[] args) {
int arr[] = new int[100];
for (int i = 0; i < arr.length; i++) {
arr[i] = i + 1;
}
arr[8] = 8;
System.out.println("—————————————插值查找——————————————");
int value = 8;
ArrayList<Integer> index = interpolationSearch(arr, 0, arr.length - 1, value);
if (index.isEmpty()) {
System.out.println("arr中没有" + value);
} else {
for (int i = 0; i < index.size(); i++) {
System.out.println("arr[" + index.get(i) + "] = " + value);
}
}
}
/** 插值查找
* @description: 如果找到返回 value 值的下标就都放在 ArrayList 里面
* @Param [arr, left, right, value]: 从 arr (有序)数组中[left, right]区间中查找 value
* @return: 返回一个 ArrayList 对象
*/
public static ArrayList<Integer> interpolationSearch(int [] arr, int left, int right, int value) {
// 如果 value 不在 arr 有序数组中则退出, 否则我们得到的 mid 可能越界
if (left > right || value < arr[0] || value > arr[arr.length - 1]) {
return new ArrayList<Integer>();
}
// 自适应
int mid = left + (right - left) * (value - arr[left]) / (arr[right] - arr[left]);
if (value < arr[mid]) { // 向左递归
return interpolationSearch(arr, left, mid - 1, value);
} else if (value > arr[mid]) { // 向右递归
return interpolationSearch(arr, mid + 1, right, value);
} else { // 找到
ArrayList<Integer> arrayList = new ArrayList<>();
// 把左边与 value 值相等的元素的下标添加到 arrayList 中
for (int i = mid - 1; i >= 0 && arr[i] == value; i--) {
arrayList.add(i);
}
// 把 mid 添加到 arrayList 中
arrayList.add(mid);
// 把右边与 value 值相等的元素的下标添加到 arrayList 中
for (int i = mid + 1; i < arr.length && arr[i] == value; i++) {
arrayList.add(i);
}
return arrayList;
}
}
}
/* Code Running Result
—————————————插值查找——————————————
arr[7] = 8
arr[8] = 8
*/
4. 斐波那契查找
由于斐波那契数列 {1, 1, 2, 3, 5, 8, 13, 21, 34, 55 } 的两个相邻数的比例无限接近黄金分割值 0.618,所以斐波那契查找(Fibonacci Search)也叫黄金分割法查找。斐波那契查找也是在有序序列中查找 value,与前两种相似,只是改变了 mid 的位置,mid 不再是中间或插值得到,而是位于黄金分割点附近。
斐波那契查找:mid = left + fi[k - 1] - 1;(其中 fi 是斐波那契数列)
代码
import java.util.ArrayList;
import java.util.Arrays;
public class FibonacciSearch {
static int maxSize = 20;
public static void main(String[] args) {
int arr[] = {1, 2, 3, 4, 5, 6, 8, 8, 8, 8};
System.out.println("—————————————斐波那契查找——————————————");
int value = 8;
ArrayList<Integer> index = fibonacciSearch(arr, value);
if (index.isEmpty()) {
System.out.println("arr中没有" + value);
} else {
for (int i = 0; i < index.size(); i++) {
System.out.println("arr[" + index.get(i) + "] = " + value);
}
}
}
/** 斐波那契查找
* @description: 如果找到返回 value 值的下标就都放在 ArrayList 里面
* @Param: [arr, value]: 从 arr (有序)数组中查找 value
* @return: 返回一个 ArrayList 对象
*/
public static ArrayList<Integer> fibonacciSearch(int [] arr, int value) {
int left = 0, right = arr.length - 1;
int k = 0; // 斐波那契分割数值的下标
int mid = 0;
int [] fi = fibonacci();
ArrayList<Integer> arrayList = new ArrayList<>(); // 保存查找结果
// 获取斐波那契分割数值的初始下标
while (right > fi[k]) {
k++;
}
// right 可能大于 arr 的长度,所以新建一个数组 temp 存放 arr 的值,后边的空间新建时为 0,要改成 arr 的最后一个元素
int [] temp = Arrays.copyOf(arr, fi[k]);
for (int i = arr.length; i < temp.length; i++) {
temp[i] = arr[right];
}
// 开始查找 value
while (left < right) {
mid = left + fi[k - 1] - 1; // 斐波那契分割(fi[k] = fi[k-1] + fi[k-2])的位置
if (value < temp[mid]) { // 从分割位置的前面找
right = mid - 1;
k -= 1;
} else if (value > temp[mid]) { // 从分割位置的后面找
left = mid + 1;
k -= 2;
} else { // 找到
if (mid > right) { // 如果在 temp 的 right 之外的空间找到
// 返回 arr 最后值的索引(right)和 right 左边和 value 相等的值添加到 arrayList中
// 把 right 和 right 左边与 value 值相等的元素的下标添加到 arrayList 中
for (int i = right; i >= 0 && arr[i] == value; i--) {
arrayList.add(i);
}
break;
} else { // 否则返回 mid 即可
// 把左边与 value 值相等的元素的下标添加到 arrayList 中
for (int i = mid - 1; i >= 0 && arr[i] == value; i--) {
arrayList.add(i);
}
// 把 mid 添加到 arrayList 中
arrayList.add(mid);
// 把右边与 value 值相等的元素的下标添加到 arrayList 中
for (int i = mid + 1; i < arr.length && arr[i] == value; i++) {
arrayList.add(i);
}
break;
}
}
}
return arrayList;
}
// 斐波那契数列
public static int[] fibonacci() {
int [] fi = new int[maxSize];
fi[0] = 1;
fi[1] = 1;
for (int i = 2; i < maxSize; i++) {
fi[i] = fi[i - 1] + fi[i - 2];
}
return fi;
}
} Code Running Result
/*
—————————————斐波那契查找——————————————
arr[6] = 8
arr[7] = 8
arr[8] = 8
arr[9] = 8
*/