二分法介绍 与 leetcode 排列硬币的三种解法

一、定义       

      二分查找又称折半查找,在处理有序且可以通过下标对元素进行访问的序列时(如有序数组),二分法会有非常好的效果。

二、思路      

      以在一长度为n的非递减数组A中查找是否有某元素K为例,二分查找的基本思路可以概括为:
       1、定义好查找的边界left、right后,首先从中间位置mid开始查找
       2、如果A[mid]的值等于K,说明已经找到该值,返回mid即可
       3、如果A[mid]的值大于K,因为数组A是非递减的,因此mid以后的元素值肯定大于等于A[mid],也就大于K;所以此时我们将该问题的搜索空间直接减半,左边界不用改,右边界改为mid-1。然后重复步骤1
       4、同理,如果A[mid]的值小于K,说明待查找元素K不可能存在于[left,mid]之间,将搜索空间减半,另left=mid+1,right不变;重复执行步骤1。
      
      示例代码如下:
 
def findK(num, K):
    left = 0
    right = len(num)    
    while(left <= right):
        mid = (left + right) // 2
        if num[mid] == K:
            return mid
        elif num[mid] < K:
            left = mid + 1
        elif num[mid] > K:
            right = mid - 1
    return -1

 

 三、leetcode441、排列硬币

     1、原题

           你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币。给定一个数字 n,找出可形成完 整阶梯行的总行数。n 是一个非负整数,并且在32位有符号整型的范围内。
        来源:力扣(LeetCode)

      2、思路

     (1)最容易想到的思路就是暴力遍历了,for循环的范围是从1到n,代码如下:
class Solution:
    def arrangeCoins(self, n: int) -> int:
        if n == 1:
            return 1
        tmp = n    # tmp记录剩余待排列的硬币数
        cnt = 0    #  记录可形成的整阶梯行的总行数
        #  i的值就表示第i个阶梯上需要放置i枚硬币
        for i in range(1, n):
            if i <= tmp:   # 若剩余待排列的硬币数多余第i行要求的硬币
                tmp -= i   # 摆好第i行,同时更新剩余的硬币数
                cnt += 1
            else:
                break
        return cnt

 

      (2)如果数学基础还记得的话,可以发现这就是等差数列求和啊,利用等差数列求和的公式可以直接求出
 
class Solution:
    def arrangeCoins(self, n: int) -> int:
        
        return int((0.25+2*n)**0.5 - 0.5)

 

       (3) 第三种思路就是二分法了。如何理解用二分法解这道题?
          根据题意,给了n个硬币,我们要做的就是看这些硬币能摆出来几个完整的阶梯。那么,对于非负整数n,它最少是1个阶梯也摆不出来,比如n=0时,因此,二分法的左边界left肯定是0,意思就是最少能摆出0个阶梯;那么最多摆出几个阶梯呢?答案是n // 2 + 1,为什么?每一个下层阶梯肯定要比上层阶梯多1,而2*(n//2 + 1) 的乘积是大于等于n的。用它做右边界,既能保证比用n作右边界要减少一些计算量,还可以保证某些特殊值情况下也会返回正确结果,如n=1的情况。
             选好边界以后,在循环体里面,mid取边界的中值,也就是说看一下mid层完整阶梯需要多少多少枚硬币。如果需要的硬币数正好等于n,则直接返回n;如果搭建mid层所需要的的硬币数超过了给定的n,那说明我们能搭建的完整阶梯数要少于n的,因此右边界更新为mid-1;同理,若搭建mid层所需要的的硬币数少于n,说明也许我们还可以再多搭建几层。这里用也许是考虑到剩余的硬币不足以再搭建一层的情况。参考代码如下:
 
def arrangeCoins(n):
    left = 0
    right = n // 2 + 1
    while(left <= right):
        mid = (left + right) // 2
        # print(left, '  ', right, '   ', mid)
        totle = 0.5 * mid * (1 + mid)
        if totle > n:
            right = mid - 1
        elif totle < n:
            left = mid + 1
        else:
            return mid
    # print(left, '   ', right)
    return right

 

      执行arrangeCoins(8),代码中的输出语句输出的内容为:
 
0    5     2
3    5     4
3    3     3
4     3

 

       重点解释一下第三行和第四行,在第三行中,左右的边界值都更新为3,此时mid的值只能取3;我们计算出搭建3层完整的阶梯需要6枚硬币,低于给定的8枚,因此左边界left会更新为4,此时循环条件被破坏,因此,最后返回的是right。
            
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值