算法:你不知道的二分查找法的那些事

看了很长时间博客,第一次自己动手写,代码和写作形式出现的问题欢迎给出建议和指正错误

在这里插入图片描述

今天分享一个非常简单的算法:二分查找法

一、什么是二分查找法

众所周知二分查找法要比暴力搜索好的多,他们都属于搜索系列的某些搜索的算法。
类型查找次数时间复杂度
二分查找法一般不超过100次O(n)
暴力搜索根据数据大小而定,在大量数据中平均次数位n/2次O(log2(n))

二、实现过程

1.原理解释

 二分查找法也可称作为折半查找,对于大量有序数据查找起来十分方便。
 
 最简单的解释方式就是小时候的猜数字游戏。
 
 类比于猜数字游戏二分查找法也是同样的原理

猜数字游戏:

  1. 首先获得一个数字20
  2. 在0-100之间猜
  3. 如果猜的值大于获得的数字,那么就会往后猜
  4. 如果猜的值小于获得的数字,那么就会往前猜
  5. 如此往复,直到猜到该数字
    嘶~

*那么我们很容易获得一个公式:(lo+li)/2

猜的次数1234567
当前猜的值:50251218211920
获得方式(li+lo)/2:(100+0)/2(50+0)/2(25+0)/2(25+12)/2(25+18)/2(21+18)/2(21+19)/2
判断值:大于20大于20小于20小于20大于20小于20等于20(结束)

可见这种方法要比纯粹的从1猜到20要好一些,特别是面对大量数据时,暴力搜索的时间复杂度为O(n)而二分查找法时间复杂度为O(log2(n))

2.代码实现(二分查找法为猜数字游戏的代码实现)

1、递归实现

/*
     * 二分查找法
     * 输入:blSearch(	(int类型)查找的n值	,	(int数组)查找的数组	,	
     *                  (int类型)数组起始点	,	(int类型)数组长度-1))
     * 输出:将要查找的值n在数组list中的下标
     */
    public int biSearch(int n,int[] list,int lo,int li){
    //lo和li的初始值分别为0和list.lenght
    	if(lo > li){
    	//如果没有这个值输出-1
    		return -1;
    	}
    	//mid为中间值,每次的中间值根据li和lo的位置判断
    	//也可写作为:
    	//int mid = (li + lo) / 2;
    	int mid=(li - lo) / 2 + lo;
    	if(list[mid] > n){
    	//如果搜索的值小于目前所获得的数组中的值
    	//那么上界缩小范围,mid-1是因为要搜索的值不包括list[mid](加不加都可)
    	//也可直接写为:
    		//return biSearch(n,list,lo,mid);
    		return biSearch(n,list,lo,mid-1);
    	}
    	//此段和上节相同
    	else if(list[mid] < n){
    		return biSearch(n,list,mid+1,li);
    	}
    	else{
    		return mid;
    	}
    }
    /*
     * 二分查找法入口
     * 输入:biSearch(	(int类型)查找的n值	,	(int数组)查找的数组	)	
     * 输出:将要查找的值n在数组list中的下标
     */
    public int biSearch(int n,int[] list){
    	return biSearch(n,list,0,list.length);
    }

2、循环实现

/*
     * 二分查找法
     * 输入:blSearch(	(int类型)查找的n值	,	(int数组)查找的数组	,	
     *                  (int类型)数组起始点	,	(int类型)数组长度-1))
     * 输出:将要查找的值n在数组list中的下标
     */
    public int biSearch(int n,int[] list,int lo,int li){
    	//创建这个mid
    	//和上面的思想基本相同
    	int mid;
    	while(lo < li){
    		mid = (lo + li) / 2;
    		if(n > list[mid]){
    			lo = mid;
    		}
    		else if(n < list[mid]){
    			li = mid;
    		}
    		else{
    			return mid;
    		}
    	}
    	return 0;
    }
    /*
     * 二分查找法入口
     * 输入:biSearch(	(int类型)查找的n值	,	(int数组)查找的数组	)	
     * 输出:将要查找的值n在数组list中的下标
     */
    public int biSearch(int n,int[] list){
    	return biSearch(n,list,0,list.length);
    }

三、总结:

在网上关于二分查找法有很多的代码与说法,实现起来也各不相同,总结出来大概分为两类:

1:使用公式(li + lo) / 2形式

	mid = (lo + li) / 2;
   	if(n > list[mid]){
  		lo = mid;
  	}
  	else if(n < list[mid]){
   		li = mid;
  	}
 	else{
   		return mid;
   	}

2:使用公式(li - lo) / 2 + lo形式

	int mid = (li-lo) / 2 + lo;
	if(list[mid] < n){
		lo = mid + 1;
	}
	else if(list[mid] > n){
		li = mid - 1;
	}
	else{
		return mid;
	}

有些同学一看,这有什么不同?
在这里插入图片描述

确实,这两种给在本质上没有不同,都是对的,但他们的表达形式不同
他们的不同之处如下:

形式考虑值
(li + lo ) / 2考虑中间值 :lo = mid或li = mid
(li - lo) / 2 + lo不考虑中间值:lo = mid + 1或li = mid - 1

可见第二种确实可能要比第一种有的时候少进行判断一次,因为
第二种没有判断mid,但第一种更容易理解

4、备注

关于二分查找法的可行性,因为二分查找法的前提是已经排列好的数组列表,如果使用二分查找法,则必须考虑数组的有序性问题,如果数组有序,那么使用二分查找法是十分高效的,但若无序则需要考虑爆搜和二分查找法的可行性问题了

下面是我对于二分查找法的数据整理

查找类型暴力搜索二分查找法
查找数据量100100
查找时间(包含排序时间)0ms0ms
查找类型暴力搜索二分查找法
查找数据量100000100000
查找时间(包含排序时间)9ms109ms

!!在100000位时居然是二分查找法所花费的时间更多,其原因是快速排序浪费了大量的时间,排序的效率要远远低于爆搜的效率

*可见,虽然二分查找法的效率很高,但其效率高是建立在数组有序的条件之下的,而数组排序要比搜索的时间长很多

*反之,虽然暴力搜索的效率很慢,但是他的搜索稳定,而且不需要排序,是暴力搜索的优势
所以,关于使用二分查找法还是使用暴力搜索确实是见仁见智了。
在这里插入图片描述


lo:起始指针
lo:末端指针
mid:搜索指针(和要查找的n进行判断)

参考资料:《算法》《java程序设计和数据结构》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值