我们一起写算法04

js实现二分法(递归和非递归)

1.算法使用范围:

二分法查找使用数据量较大的时候,但是条件是数据需要先排好顺序。
在这里插入图片描述

2.算法描述:

  1. 确定该区间的中间位置,每次查找中间的值,数组被分为左边和右边两部分。

  2. 将查找的值与中间值比较。

    (1)如果相等,就返回对应的下标。

    (2)如果查找的值大于中间的值,则将右边部分数组继续分成左右两部分,确定中间位置的值,继续步骤2。

    (3)如果小于,则在数组左半边继续二分查找。

  3. 重复上述步骤,直到找到要查找的值位置。

    总而言之,每一次查找与中间值比较,判断是否查找成功,不成功当前查找区间缩小一半,循环查找,即可!!!

    使用beautiful_lady的博文图解截图如下:
    在这里插入图片描述

3.二分法的时间复杂度

时间复杂度:O(log2n)。

  1. 最坏情况是查找到最后一个元素(或者第一个元素),Master定理T(n)=T(n/2)+O(1),所以时间复杂度T(n)=O(log2n)

  2. 最好情况是查找的元素等于中间元素,只需要比较一次(奇数长度数列的正中间,偶数长度数列的中间靠左的元素)。

4.while循环实现代码实现

while循环实现二分查找是非递归的方式,实现代码如下:

function divisionTwo(arr,searchNum){
	//获取数组长度
	var len = arr.length;
	//起始位置
	var beginIndex = 0;
	//结束位置
	var lastIndex = len - 1;
	//其实位置总是小于等于结束位置的
	while(beginIndex<=lastIndex){
		//中间位置
		//floor(x):返回小于等于x的最大整数
		 var mid =Math.floor((beginIndex+lastIndex)/2) ;
		 //查找的值searchNum与中间值arr[mid]相比较
		 if (searchNum == arr[mid]){
		 	return mid
		 }else if(searchNum > arr[mid]){
		 	//如果查找的值searchNum大于中间值,在右侧使用二分法,
		 	beginIndex = mid +1;
		 }else{
		 	//如果查找的值searchNum小于中间值,在左侧使用二分法
		 	lastIndex = mid -1;
		 }
	}
	return arr;
}
//例如要在arry数组中查找数字18
var arry = [1,2,3,5,7,11,15,18,22,45]
console.log(divisionTwo(arry,18))
console.log(arry[divisionTwo(arry,18)])

输出结果:

在这里插入图片描述

5.递归算法代码实现

要想理解递归实现二分法,首先要理解什么是递归。

  1. 递归算法

    概念:函数通过直接调用自身,或者两个函数之间的互相调用,来达到一定的目的,比如排序,阶乘等。

    在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。

    递归次数过多容易造成栈溢出。

    阶乘是最简单的递归,如下:

    function factorial(n) {
      if (n == 0) {
        return 1;
      } else {
        return n * factorial(n - 1);
      }
    }
    

    上面程序,n不等于0时,factorial函数调用自身,此时参数传入n-1,即第一次是返回n*(n-1),第二次是返回n*(n-1)*(n-2)。。。。依次类推,直到n等于0,返回1。这个思想就是递归。

    有两个关键点:

    (1)函数调用自身

    (2)函数的参数要做缓存,下次调用的时候必须是上一次调用参数变化后的值,否则不做缓存,每次调用自身的时候,n都是初始化时设置的那个值,导致输出结果错误。

  2. 递归优化

    递归优化: 函数可以用对象去记住先前操纵的成果,从而能避免无谓的运算,避免重复工作,将执行过的运算或操作,缓存起来,如果后续有相同的操作可直接从缓存中查找,没有相同操作则进行递归,可大大减少递归的工作量,提高性能。

  3. 递归实现二分法

    递归的方法实现二分查找,代码如下:

    //递归方式
    function divisionTwo(arr,searchNum,beginIndex,lastIndex){
    	//获取数组长度
    	var len = arr.length;
    	//起始位置,参数缓存,下次调用时使用
    	beginIndex = beginIndex||0;
    	//结束位置,参数缓存,下次调用时使用
    	lastIndex = lastIndex||len - 1;
    	//中间位置
    	//floor(x):返回小于等于x的最大整数
    	var mid =Math.floor((beginIndex+lastIndex)/2) ;
    	
    	//searchNum与中间值arr[mid]相比较,一直没有找到要查找的元素
    	//beginIndex值会增大,lastIndex值会减小,beginIndex > lastIndex时说明查找的元素不在数组中,返回-1
    	if (beginIndex > lastIndex) {
            return -1;
        }
    	    
    	//查找的值searchNum与中间值arr[mid]相比较
    	if (searchNum == arr[mid]){
    	 	return mid
    	}else if(searchNum > arr[mid]){
    	 	//查找的值searchNum大于中间值,在右侧查找,起始位置想右侧移动mid+1位
    	 	beginIndex = mid +1;
    	 	
    	}else{
    	 	//查找的值searchNum小于中间值,在左侧查找,结束位置向左侧移动mid-1位
    	 	lastIndex = mid -1;
    	}
    	//递归,函数调用自身
     	return divisionTwo(arr,searchNum,beginIndex,lastIndex)
    }
    //例如要在arry数组中查找数字18
    var arry = [1,2,3,5,7,11,15,18,22,45]
    console.log(divisionTwo(arry,18))//7
    console.log(arry[divisionTwo(arry,18)])//18
    

    分析:上面程序与while循环实现二分查找的不同之处就是取消了while循环,使用递归调用自身函数代替。while循环实现二分查找是非递归的方法。

    输出结果分析:
    在这里插入图片描述

对于有序字符串也可以使用二分查找。如下:

在这里插入图片描述

对数字和字符串混合的数组也可以使用二分查找算法,建议要各自排好序。如下:
在这里插入图片描述

每天进步一点点、充实一点点、加油!!!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值