算法与数据结构之美——二分查找

开篇思考

如何用最省内存的方式实现快速查找功能?
思考题:
对于1000万个整数数据,每个数据占8个字节,如何快速的判断某个整数是否出现在这1000万数据中呢? 内存空间不超过100M;

二分查找(Binary Search)

二分查找针对于有序集合的查找方法,每次都与区间的中间数据比对大小,缩小查找区间的范围,下图是一次二分查找的过程,low和high代表的是待查找区间的下标,mid表示待查找区间的中间元素下标。
二分查找过程

性能分析

二分查找高效,假设数据大小为n,每次查找完数据之后都会缩小为原来的一般,就是除以2,最坏的情况,直到查找区间缩小为空,才会停止。
二分查找区间大小变化
当n/2^k=1时,k=log2(n),所以时间复杂度就是O(logn);

代码实现


二分查找非递归算法

public int bSearch(int[] a,int value){
	//设定low和high指针
	int low = 0int high = a.length - 1;
	
	while(low<=high){
		int mid = low + (high-low)>>1;
		if(a[mid]==value)
		{
			return mid;
		}else if(a[mid]<value){
			low = mid + 1;
		}else{
			high = mid - 1;
		}
	}
	return -1;
	
}
  1. 循环退出条件 low<=high
  2. mid取值,如果想要将性能优化到极致的话,可以写成mid = low+((high-low)>>1);
  3. low和high的更新; low = mid + 1,high = mid - 1;

二分查找递归算法

public int bSearch(int[]nums,int value,int low,int high){
	if(low>high) return -1;
	int mid = low + (high - low)>>1;
	if(nums[mid]==value)
		return mid;
	else if(nums[mid]>value)
		return bSerarch(nums,value,mid+1,high);
	else 
		return bSearch(nums,value,low,mid-1);	
}

应用场景

  • 二分查找应用场景具有局限性,依赖顺序表结构,就是数组;
  • 二分查找针对的有序数据;
  • 数据量太小不适合二分查找;

二分查找变形问题

变体一:查找第一个值等于给定值的元素

代码分析:
a[mid]与要查找的value之间的关系就是:大于、小于、等于;
对于大于,只需要更新 high = mid - 1;
对于小于,只需要更新 low = mid +1;
当a[mid] = value的时候,需要判断是否是第一个值等于value:

  1. 当mid = 0时,肯定是第一个值;
  2. 当mid - 1的值不等于value,那么返回mid即可;
  3. 当mid - 1的值等于value,那么要找的元素肯定位于[low,mid-1]这个区间内,所以需要将high更新为mid -1 ;
//二分查找第一个值等于给定值的元素
public int bSearch(int[] nums,int value){
	int low = 0;
	int high = nums.length - 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{
			//判断mid-1是否等于value
			if(mid == 0||a[mid-1]!=value)
				return mid;
			else{
				high = mid - 1;
			}	
		}		
	}
	return -1;
}

变体二:查找最后一个值等于给定值的元素

类似于变体一,那我直接call 代码啦!

public int bSearch(int[]nums,int value){
	int low = 0;
	int high = nums.length - 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==nums.length-1||a[mid]+1!=value)
				return mid;
			else 
				low = mid + 1;	
		}		
	}
	return -1;
}

变体三:查找第一个大于等于给定值的元素

  • 如果a[mid]<value,那么要查找的元素肯定在[mid+1,high]之间,更新low = mid+1;
  • 如果a[mid]>=value,如果mid ==0,则a[mid]前面没有元素或者a[mid-1]<value,则直接返回mid,反之,第一个大于给定给定值的元素,存在于[low,mid-1]内,更新high为mid-1;

话不多说,直接code

public int bSearch(int[]nums,int value){
	int low = 0;
	iny high = nums.length - 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 bSearch(int[]nums,int value)
{
	int low = 0;
	int high = nums.length - 1;
	while(low<=high)
	{
		int mid = low +((high-mid)>>1);
		if(a[mid]<=value)
		{
			if(mid==nums.length-1||a[mid]+1>value)
				return mid;
			else
				low = mid + 1;	
		}else{
			high = mid - 1;
		}
	}
	return -1;
}

解答开篇

如何在1000万个整数中快速查找某个整数?
内存限制是100M,每个数据大小是8个字节,那么所有数据存储到数组中占据80M的内存,符合内存的限制,先对1000万个数据进行排序,然后再利用二分查找进行查找;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值