二分查找的思想
查找0–99之间的数字,如果查找一个数字,我们每次取中间middle数判断,如果中间数大了,就像0–middle区间找,如果小了就像middle–99区间找,假如我们查找的是23
二分查找是针对的有序集合,每次都跟区间中间元素的对比,将待查找的区间缩小一半,直到找到元素,或者区间缩小到0
二分查找的时间复杂度O(logn)
假设查找的区间为n,每次缩小一半,最坏情况下直到缩小到0,才结束
当n/2k=1时,K就是总共缩小的次数,每次缩小涉及元素的一次比较,经过k此缩小,时间复杂度就是O(k),通过n/2k=1,那么k=log(n),所以时间复杂度为O(logn),这是一种极其高效的时间复杂度
二分查找的简单实现
public int bsearch(int[] a, int n, int value) {
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (a[mid] == value) {
return mid;
} else if (a[mid] < value) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
二分查找的局限性
1 二分查找依赖数组
是否可以使用链表,答案不可以,二分查找主要用了根据下标O(1)时间复杂度,随机访问数据
2 二分查找针对的是有序数据
3 数据量太小,不适合二分查找
太小的话,直接顺序遍历就行了
4 数据量太大,也不适合二分查找
因为我二分查找依赖数组,所以需要连续的内存空间,假如1G的数据,需要1G的连续内存空间,这个不现实
二分查找的变形
上面实现的二分查找没有考虑到,数组有重复的数据的情况,下面我们就对这种情况分析一下
变体一 查找第一个等于这个元素的数据
这个里面存在重复元素,我们希望找到第一个等于8的值,也就是下标为5的值,但不希望下标是6和7
看下实现
public int bsearch(int[] a, int n, int value) {
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (a[mid] > value) {
high = mid - 1;
} else if (a[mid] < value) {
low = mid + 1;
} else {
if ((mid == 0) || (a[mid - 1] != value)) return mid;
else high = mid - 1;
}
}
return -1;
}
每当数值和查找的数据相等时,如果数据已经是第一个了或者mid的上一个数据不等于这个值,就直接返回,否则就意味着有前边有重复的数据,那就把high=mid-1,继续查找
变体二 查找最后一个等于该值得元素
public int bsearch(int[] a, int n, int value) {
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (a[mid] > value) {
high = mid - 1;
} else if (a[mid] < value) {
low = mid + 1;
} else {
if ((mid == n - 1) || (a[mid + 1] != value)) return mid;
else low = mid + 1;
}
}
return -1;
}
思路跟上方一样
变体三 查找第一个大于等于给定值的元素
public int bsearch(int[] a, int n, int value) {
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (a[mid] >= value) {
if ((mid == 0) || (a[mid - 1] < value)) return mid;
else high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
变体四 查找最后一个小于等于给定值得元素
public int bsearch7(int[] a, int n, int value) {
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (a[mid] > value) {
high = mid - 1;
} else {
if ((mid == n - 1) || (a[mid + 1] > value)) return mid;
else low = mid + 1;
}
}
return -1;
}