1.线性查找
线性查找将要查找的关键字和数组中的元素逐个比较,直到找到与关键字匹配的元素,或者查找结束也没有找到关键字为止。如果匹配成功,则返回元素在数组中的下标。
public static int linearSearch(int arr[], int key) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == key) {
return i;
}
}
return -1;
}
假设数组有1024个元素,最佳情况是key在第一个位置,只需要比较1次,最坏情况是比较1023次,所以线性查找的时间复杂度是O(n).
2.二分查找
二分查找的前提是数组中的元素必须是有序的。假设数组升序排序,二分查找将关键字与数组的中间元素进行比较,有以下3种情况:
1.关键字小于中间元素,在数组的前一半元素中继续查找关键字。
2.关键字和中间元素相等,匹配成功,结束查找。
3.关键字大于中间元素,在数组的后一半元素中继续查找关键字。
时间复杂度:
二分查找在每次比较之后排除一半或者一半+1的数组元素。假设数组有n个元素,经过第一次比较,剩下n/2个元素,第二次比较,剩下n/2/2个元素,经过k次查找之后,剩下n/2的k次方个元素需要查找,当k = log2(n)时,数组中只剩下一个元素,最后只需要再比较log2(n)+1次,时间复杂度为O(logn)。与线性查找相比,1024个元素的数组最坏情况下只需要比较log2(1024)+1=11次,而线性查找最坏需要比较1023次。
实现思路:
从第一次迭代的实现开始,将关键字key和低下标low为0,高下标high为arr.length-1的中间元素比较
int low = 0;
int high = arr.length - 1;
int mid = (low + high)/2;
if (key < arr[mid]) {
high = mid - 1;
} else if (key == arr[mid]) {
return mid;
} else {
low = mid + 1;
}
第二部,在外层增加一个循环,重复查找,缩小范围,注意循环条件low <= high,如果条件是low<high,则当数组中只有1个元素时,查找会漏掉这个元素。
int low = 0;
int high = arr.length - 1;
while (low <= high) {
int mid = (low + high)/2;
if (key < arr[mid]) {
high = mid - 1;
} else if (key == arr[mid]) {
return mid;
} else {
low = mid + 1;
}
}
当没有查找到key时,可以考虑返回key值的插入位置,low的位置是插入点,在这个位置插入元素可以保持数组的有序性,一般返回负数,表示这个位置不在数组中,一般情况下,可以返回-low,但是当查找的元素小于数组的最小值时,查找结束时low的值为0,-0 = 0,会产生元素在位置0的歧义,最好的方式是返回-low-1.表示在-low-1的位置上(非下标)插入
完整实现:
public class Search {
public static void main(String[] args) {
int[] arr = {3,4,12,22,33,40,54,82,99,1023};
System.out.println("binarySearch(arr, 22)=" + binarySearch(arr, 22));
System.out.println("binarySearch(arr, 880)=" + binarySearch(arr, 880));
}
public static int binarySearch(int arr[], int key) {
int low = 0;
int high = arr.length - 1;
while (low <= high) {
int mid = (low + high)/2;
if (key < arr[mid]) {
high = mid - 1;
} else if (key == arr[mid]) {
return mid;
} else {
low = mid + 1;
}
}
return -low -1;
}
}
输出:
binarySearch(arr, 22)=3
binarySearch(arr, 880)=-10