1.解决上一篇的问题
问题并不难,首先要求的内存是小于100M,1000万的数据每个数据八个字节,全部存入数组占用的内存大概也是80M,所以完全可以使用二分查找来实现。
2.复杂的二分查找
简单的二分查找我们都会,我们的假设也是数组中没有相同元素,假设数组中有相同的元素,查找第一个值等于给定值的元素呢?查找最后一个值等于给定值的元素呢?显而易见我们之前的代码已经不适合这两种问题了。先来一段比较烧脑的代码解决查找第一个值等于给定元素的值:
int low = 0;
int high =n-1;
while (low<=high){
int mid = ((high-low)>>1)+low;
if(arr[mid]<k){
low =mid+1;
}else if(arr[mid]>=k){
high=mid-1;
}
}
if(arr[low]==k)return low;
else return -1;
}
上边的代码是不是特别不好理解,我也是绕了好久才绕出来,接下来我们换一种思路来实现:
public static int binarySearchFirstK2(int[] arr,int n,int k){
int low =0;
int high = n-1;
while (low<=high){
int mid =low + ((high-low)>>1);
if(arr[mid]<k){
low=mid+1;
}else if(arr[mid]>k){
high=mid-1;
}else {
if(mid==0||arr[mid-1]!=k)return mid;
else high=mid-1;
}
}
return -1;
}
上边的代码只是将等于的情况分离出来,并且在适合跳出循环的时候跳出,个人觉得这种代码可读性强,逻辑清晰,bug相对来说较少,而且减少了多余的比较。
接下来解决最后一个等于给定元素的值就直接上代码了:
public static int binarySearchLastK(int[] arr,int n,int k){
int low = 0;
int high = n-1;
while (low<=high){
int mid = low + ((high-low)>>1);
if(arr[mid]<k){
low=mid+1;
}else if(arr[mid]>k){
high=mid-1;
}else {
if(mid==n-1||arr[mid+1]!=k)return mid;
else low=mid+1;
}
}
return -1;
}
上边两个问题弄明白后,那么我们要查找第一个大于等于给定值的元素?或者最后一个小于等于给定值的元素怎么处理呢?其实逻辑很简单就是将等于的情况分别与arr[mid]<k和arr[mid]>k的情况合并即可,代码如下:
public static int binarySearchFirstMoreOrEqualThanK(int[]arr,int n,int k){
int low = 0;
int high = n-1;
while (low<=high){
int mid = low + ((high-low)>>1);
if(arr[mid]>=k){
if(mid==0||arr[mid-1]<k)return mid;
high = mid-1;
}else if(arr[mid]<k){
low= mid +1;
}
}
return -1;
}
public static int binarySearchLastLessOrEqualThanK(int[] arr,int n,int k){
int low = 0;
int high = n-1;
while (low<=high){
int mid = low + ((high-low)>>1);
if(arr[mid]<=k){
if(mid==n-1||arr[mid+1]>k)return mid;
else low=mid+1;
}else if(arr[mid]>k){
high=mid-1;
}
}
return -1;
}
问题
如何查找一个ip所在的区域?
总结
其实实际情况中能够使用二分查找的情况我们都更倾向于使用散列表或者二叉树来进行处理,那么二分查找的用处呢?
其实二分查找更适合于“近似查找”的问题。在这种问题的处理上二分查找要明显好于散列表和二叉树等。