PS:本文系转载文章,阅读原文可读性会更好,文章末尾有原文链接
ps:本篇文章写线性查找算法、二分查找算法和插值查找算法。
1、线性查找算法
线性查找算法是最简单的查找算法,它的思路就是:对一组有序或者无序的序列进行遍历,逐个比较,如果要查找的值和序列中的某个值相等,那么证明已经找到,就返回这个值在序列中的索引;如果要查找的值遍历完序列后还是没有一个与之相等的值,那么就返回 -1。
我们来举个例子,假设有一个 int 类型数组 a={2,4,3,5,1,8,9},我们从这个数组 a 中找 1 和 0,那么代码如下所示:
public class Test8 {
/**
- @param args
*/
public static void main(String[] args) {
int[] a = {2,4,3,5,1,8,9};
int result = linearSearch(a,1);
int result2 = linearSearch(a,0);
System.out.println(result == -1 ? "没有找到1" : ("1在数组a中的位置为:" + result));
System.out.println(result2 == -1 ? "没有找到0" : ("0在数组a中的位置为:" + result2));
}
/**
- @param arr 要查找的数组
- @param findVal 要查找的值
@return 返回对应值在数组中的下标,如果没有返回 -1
*/
public static int linearSearch(int[] arr, int findVal) {
int result = -1;
if (arr == null || arr.length <= 0) {
result = -1;
} else {
for (int i = 0; i < arr.length; i++) {if (arr[i] == findVal) { result = i; break; }
}
}
return result;
}
}
运行结果如下所示:
1在数组a中的位置为:4
没有找到0
2、二分查找算法
二分查找算法又叫折半查找算法,能够进行二分查找算法是有要求的:它是一个有序数列且存放是按照从小到大进行的。
我们用 start 表示数列起始位置下标,用 end 表示数列终止位置下标,mid 表示数列中间位置下标,用 findVal 表示待查找元素,用 a 表示数组名称;在已经排好序的序列中,通过 mid=(start+end)/2 计算出中间位置,通过 findVal 与 mid 进行比较,如果相等,那么就证明已经找到;如果 findVal 比 a[mid] 小,那么将 end = mid -1,也就是将 end 移动到 mid 左边一个位置,并且重新计算 mid;如果比 a[mid] 大,那么将 start = mid + 1 ,也就是将 start 移动到 mid 右边一个位置,并且重新计算 mid;一直循环查找,直到 start > end,证明没找到对应的元素,停止循环。
我们举个例子再详细分析一下,假设我有一个有序数列且大小都是从小到大排列的数组 a={1, 2, 3, 4, 5, 6, 7, 8, 9, 10},一开始 start = 0,end = 9,mid = (start + end) / 2 = (0 + 9) / 2 = 4,a[mid] = a[4] = 5 。
2、1 假设查找 4
(1)因为 4 < a[mid] = a[4] = 5,所以将 end 移动到 mid 左边一个位置,end = mid - 1 = 4 - 1 = 3 。
(2)重新计算 mid,mid = (start + end)/ 2 = (0 + 3) / 2 = 1;因为 4 > a[mid] = a[1] = 2,所以将 start 移动到 mid 右边一个位置,start = mid + 1 = 1 + 1 = 2 。
(3)重新计算 mid,mid = (start + end) / 2 = (2 + 3) / 2 = 2;因为 4 > a[mid] = a[2] = 3,所以将 start 移动到 mid 右边一个位置,start = mid + 1 = 2 + 1 = 3 。
(4)重新计算 mid,mid = (start + end) / 2 = (3 + 3) / 2 = 3;因为 4 = a[mid] = a[3] = 4,所以已经找到,退出循环。
2、2 假设查找 11
(1)因为 11 > a[mid] = a[4] = 5,所以将 start 移动到 mid 右边一个位置,start = mid + 1 = 4 + 1 = 5 。
(2)重新计算 mid,mid = (start + end)/ 2 = (5 + 9) / 2 = 7;因为 11 > a[mid] = a[7] = 8,所以将 start 移动到 mid 右边一个位置,start = mid + 1 = 7 + 1 = 8 。
(3)重新计算 mid,mid = (start + end)/ 2 = (8 + 9) / 2 = 8;因为 11 > a[mid] = a[8] = 9,所以将 start 移动到 mid 右边一个位置,start = mid + 1 = 8 + 1 = 9 。
(4)重新计算 mid,mid = (start + end)/ 2 = (9 + 9) / 2 = 9;因为 11 > a[mid] = a[9] = 10,所以将 start 移动到 mid 右边一个位置,start = mid + 1 = 9 + 1 = 10 。
(5)start = 10,end = 9,start > end,说明没有找到,所以退出循环。
我们用代码实现一下:
public class Test8 {
public static void main(String[] args) {
int[] a = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
binarySearch(a, 4);
binarySearch(a, 11);
}
private static int binarySearch(int[] a, int findValue) {
int result = -1;
if (a != null && a.length > 0) {
int mid = 0;
int start = 0;
int end = a.length - 1;
while (true) {
mid = (start + end) / 2;
if (start > end) {
System.out.println("未从数组a中找到" + findValue);
break;
} else if (findValue > a[mid]) {
start = mid + 1;
} else if (findValue < a[mid]) {
end = mid - 1;
} else {
result = mid;
System.out.println("从数组a中已经找到了" + findValue + ",它在第"
+ result + "个位置");
break;
}
}
} else {
System.out.println("数组a为空或者没有一个数据");
}
return result;
}
}
程序运行后,日志打印如下所示
从数组a中已经找到了4,它在第3个位置
未从数组a中找到11
3、插值查找算法
插值查找算法类似二分查找算法,它查找的数列必须是一个有序数列且存放是按照从小到大进行的。
我们用 start 表示数列起始位置的下标,用 end 表示数列终止位置的下标,findValue 表示要查找的数,用 arr 表示数组的名称,mid 表示 start 和 end 闭区间的一个数,它有可能是中间数,也有可能不是;对于插值查找算法,mid 的计算公式为:
int mid = start + (end - start) * (findValue - arr[start]) / (arr[end] - arr[start])
在已经排好序的序列中,先判断 arr[0] > findValue 又或者 findValue > arr[arr.length - 1] 这2个条件是否成立,如果不成立再计算 mid 并通过 findValue 与 mid 进行比较,如果相等,那么就证明已经找到,就退出循环;如果 findValue 比 arr[mid] 小,那么将 end = mid -1,也就是将 end 移动到 mid 左边一个位置,并且重新计算 mid;如果比 arr[mid] 大,那么将 start = mid + 1 ,也就是将 start 移动到 mid 右边一个位置,并且重新计算 mid;一直循环查找,直到 start > end,证明没找到对应的元素,停止循环。
我们举个例子再详细分析一下,假设我有一个数组 arr = {2, 12, 22, 32, 42, 52, 62, 72, 82, 92},一开始 start = 0,end = 9 。
3、1 假设查找 82
(1)arr[0] > findValue 又或者 findValue > arr[arr.length - 1] 这2个条件没有一个成立,那么计算 mid,mid=start + (end - start) (findValue - arr[start]) / (arr[end] - arr[start])= 0 + (9 - 0) (82 -2) / (92 - 2) = 8 。
(2)做比较,mid = 8,findValue = arr[8] = 82,正面2已经找到,退出循环。
3、2 假设查找 102
(1)findValue > arr[arr.length - 1] = arr[9] = 92,所以证明没有找到,结束查找 。
我们用代码实现一下:
public class Test8 {
public static void main(String[] args) {
int[] arr = { 2, 12, 22, 32, 42, 52, 62, 72, 82, 92 };
insertSearch(arr, 82);
insertSearch(arr, 102);
}
public static int insertSearch(int[] arr, int findValue) {
int result = -1;
if (arr != null && arr.length > 0) {
if (findValue < arr[0] || findValue > arr[arr.length - 1]) {
System.out.println("在arr数组中没有找到" + findValue);
} else {
int start = 0;
int end = arr.length - 1;
int mid = 0;
int count = 0;
while (true) {
count++;
System.out.println("第" + count + "次查找");
mid = start + (end - start) * (findValue - arr[start])
/ (arr[end] - arr[start]);
if (start > end) {
System.out.println("在arr数组中没有找到" + findValue);
break;
} else if (findValue > arr[mid]) {
start = mid + 1;
} else if (findValue < arr[mid]) {
end = mid - 1;
} else {
result = mid;
System.out.println("在arr数组中已经找到了" + findValue + ",它在第"
+ mid + "个位置");
break;
}
}
}
} else {
System.out.println("arr数组为空或者元素个数为0");
}
return result;
}
}
运行程序,日志打印如下所示:
第1次查找
在arr数组中已经找到了82,它在第8个位置
在arr数组中没有找到102