浅谈二分查找

浅谈二分查找

最近看了二分查找的一些内容,记录一下。不要小看二分查找,看似简单的二分查找其实是暗藏玄机的。

本文参考极客时间王争《数据结构与算法之美》

0.什么是二分查找

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。(摘自百度百科)

若你还是不理解,其实生活中还是有很多用处的。下面给你讲个故事

学计算机的小张同学,从图书馆借了很多关于计算机方面的书籍,想要回去好好学习一下。可就要抱着书本出图书馆门的时候,门禁滴滴滴的响了。图书馆的阿姨赶忙过来询问小张,小张也很懵逼呀。明明所有的书都是刷过的啊。这时阿姨说,可能借书的机器不好用了,找出书来在去借一下就好了。这时候小张开始漫长的找书,一本一本的从门禁中穿过。看的图书馆阿姨都累了。这时阿姨拿过所有的书,把书分成平均两份,从门禁穿过,有一份响了。然后再将响的那一份,分成两份。就这样,阿姨不用几次就找到了没刷成功的书了。看的小张都惊呆了。阿姨对小张说:“这是二分法查找,你刚刚一本一本的找时间复杂度太高了都O(n)了,用二分查找时间复杂度可是O(logn)呢”。小张吃惊,心想图书馆工作人员也是深藏不漏啊

1.二分查找代码实现

其实,二分查找的代码还是比较简单的,不管是递归的还是非递归的。

//非递归二分查找
	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;

	}

//递归的二分查找
	public int bsearch(int []a, int n, int value){
		return bsearchInternally(a,0,n-1, value);
	}

	private int bsearchInternally(int []a,int low ,int high,int value){
		if(low > high) return -1;
		mid = low + ((high - low) >> 1);

		if(value == a[mid]){
			return mid;
		}else if(value > a[mid]){
			return bsearchInternally(a,mid+1,high,value);
		}else{
			return return bsearchInternally(a,low,mid-1,value);
		}
	}

这里有三点说一下

  • 注意在非递归遍历的时候循环条件是low <= high 是有等号的;

  • 在求mid的时候为了防止low+high可能会发生溢出,可换用这种写法 mid = low + (high - low)/2;若追求更加极致的速

    度的话,可将除法改为位运算就是递归算法中mid的求法mid = low + ((high - low) >> 1);

  • 在这就是mid的更新,注意别搞混了。

2.二分查找的局限性

1.二分查找依赖的是顺序表结构,简单点说就是数组。

2.二分查找针对的是有序的数组。

3.二分查找不适合数据量太小和太大的情况。

3.二分查找的应用

  • 查找第一个值等于给定值的元素
//查找第一个值等于给定值的元素
	public int bsearch(int []a,int n,int value){

		int low = 0;
		int high = n - 1;

		while(low <= high){
			int mid = low + (high - low)/2;

			if(value > a[mid]){
				low = mid + 1;
			}else if(value < a[mid]){
				high = mid -1;
			}else{
				if(mid == 0 || (a[mid -1] != value)) return mid;
				else high = mid - 1;
			}
		}

		return -1;

	}

其实代码还是比较好理解的,在查找的过程中是是以基本的额二分查找为基础的,就是在判断相等时,以前是直接相等即可返回,现在则是mid==0,显然已经是第一个元素了或者是a[mid - 1] !=value则就是定值的第一个元素了

  • 查找最后一个值等于给定值的元素

显然有了第一个的讲解这个基本是比较容易的,直接上代码。

//查找最后一个值等于给定值的元素
	public int bsearch(int []a,int n,int value){

		int low = 0;
		int high = n - 1;

		while(low <= high){
			int mid = low + (high - low)/2;

			if(value > a[mid]){
				low = mid + 1;
			}else if(value < a[mid]){
				high = 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){
		 	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;
	}

这个代码在整体思路是上还是和上一个基本是一致的,一个大于等于的元素,则就是前面的都比value值小。这样作为返回的条件极为正确。

  • 查找最后一个小于等于给定值的元素

    //查找最后一个小于等于给定值的元素
    	public int bsearch(int []a, int n,int value){
    		itn low = 0;
    		int high = n - 1;
    
    		while(low <= high){
    
    			mid = low + ((high-low)>>1);
    			if(a[mid] <= value){
    				if(mid = n-1 || a[mid + 1] >value) return mid;
    				else low = mid + 1;
    			}else{
    				high = mid - 1;
    			}
    
    		}
    
    		return -1;
    	}
    
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值