二分查找,如何用最胜内存的方式实现快速查找
针对有序数据集合的查找算法,二分查找。
问题:假设我们有1000万个整数数据,每个数据占8个字节,如何设计数据结构和算法快速判断某个整数是否出现在这1000万数据中,
最简单的办法就是将数据存储在数组中,内存占用差不多80M,符合内存限制,所以,先对数据从小到大排序,然后利用二分查找算法,就可以快速查找想要的数据。
二分查找针对的是一个有序的数据集合,查找思想类似分治思想,每次都通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间缩小为0
二分查找时间复杂度低,O(logn),
二分查找最简单的就是有序数组中不存在重复元素。
二分查找依赖的是顺序表结构,就是数组。还针对的是有序数据。
二分查找只能再插入,删除不频繁,一次排序多次查找的场景中,针对动态变化的数据集合,二分查找将不再适用。
数据量太小不适合二分查找。
数据量太大也不适合二分查找,因为底层依赖数组这种数据结构,而数组为了支持随机访问,要求内存空间连续,对内存的要求苛刻。
使用递归实现二分查找
public static int recursionBinarySearch(int[] arr,int key,int low,int high){
if(key arr[high] || low > high){
return -1;
}
int middle = low + ((high - low)>>1);
//middle = (low + high) / 2; 这种写法有可能会导致溢出,比如low很大,high也很大,之和就溢出了。
if(arr[middle] > key){
return recursionBinarySearch(arr,key,low,middle);
}
else if(arr[middle] < key){
return recursionBinarySearch(arr,key,middle + 1,high);
}else {
return middle;
}
}
普通的二分查找。
public static int commonBinarySearch(int[] arr,int key){
int low = 0;
int high = arr.length -1;
int middle = 0;
if(key < arr[low] || key> arr[high] || low > high){
return -1;
}
while(low <=high){
middle = low + ((high - low)>>1);
//middle = (low + high) / 2; 这种写法有可能会导致溢出,比如low很大,high也很大,之和就溢出了。
if(arr[middle] > key){
high = middle -1;
}else if(arr[middle] < key){
low = middle + 1;
}else {
return middle;
}
}
return -1;
}
变体一,查找第一个值等于给定值的元素
public static int bsearchFirst(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 {
low = mid + 1;
}
}
if (low < n && a[low]==value) return low;
else 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) {
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;
}
变体二,查找第一个值等于给定值的元素
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 bsearchFour(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;
}
如果有序数组是一个循环有序数组,比如4.5.6,1,2,3这种情况。
//无重复元素
public int search(int[] nums, int target)
{
int start = 0,end = nums.length-1;
while(start<=end)
{
int mid = (start+end)/2;
if(target == nums[mid]){return mid;}
if(nums[start]<=nums[mid])//说明start-mid之间是有序的
{
if(nums[start]<=target && targetnums[mid] && target<=nums[end])
{
start = mid+1;
}else
end = mid-1;
}
}
return -1;
}
//有重复数字
public boolean searchCycle(int[] nums, int target) {
int start = 0,end = nums.length-1;
while(start<=end)
{
int mid = (start+end)/2;
if(target == nums[mid]){return true;}
if(nums[mid]>nums[start])
{
if(target>=nums[start] && targetnums[mid] && target<=nums[end])
{
start = mid+1;
}else
end = mid-1;
}
}
return false;
}