二分搜索算法(以力扣周赛“每个小孩最多分到多少糖果”为例)

2023-05-16

上午刚刚打完力扣周赛,感觉心有余而力不足,好多次都有这么一种差一点就AC的感觉,所以归根结底还是自己基础不牢的缘故,借着有感记录下二分搜索算法的解法,也祭奠下自己第287场周赛的再次失败!

二分搜索算法的关键在于:

(1)定义好左、右侧边界,左侧边界是否包含,右侧边界是否包含。

(2)考虑好while循环条件,是在left<right还是left<=right。

(3)根据(1)中左、右侧边界是否包含的设置,在收缩的过程中处理好加一,减一的问题。

强烈推荐如下的二分搜索算法模板,:

搜索右侧边界:

int left = 左侧边界(包含)

int right = 右侧边界(不包含)

while(left < right){//循环条件在搜索左、右侧边界时两者相同时退出即可
                    //因为设置的范围为左闭右开,没必要等到两者相等

    int mid = left + (right-left)/2

    if(number[mid] == target)

        left = mid+1;//找到符合要求的数后别着急返回,收缩左边界继续搜索右边界

    else if(number[mid] > target)

        right = mid;//数过大,收缩右边界(因刚开始设置为右侧边界不包含,所以不加1)

    else if(number[mid] < target)

        left = mid+1;//数过小,收缩左边界(因刚开始设置为左侧边界包含,所以需要加1)

}

    return left-1;//返回mid,mid =  right-1(因为右侧边界是不包含的) = left-1;

搜索左侧边界:

 

int left = 左侧边界(包含)

int right = 右侧边界(不包含)

while(left < right){//循环条件在搜索左、右侧边界时两者相同时退出即可
                    //因为设置的范围为左闭右开,没必要等到两者相等

    int mid = left + (right-left)/2

    if(number[mid] == target)

        right = mid;//找到符合要求的数后别着急返回,收缩右边界继续搜索左边界

    else if(number[mid] > target)

        right = mid;//数过大,收缩右边界(因刚开始设置为右侧边界不包含,所以不加1)

    else if(number[mid] < target)

        left = mid+1;//数过小,收缩左边界(因刚开始设置为左侧边界包含,所以需要加1)

}

    return left;//返回mid,mid = left(因为左侧边界包含) = right;

 搜索某个数:

int left = 左侧边界(包含)

int right = 右侧边界(包含)

while(left <= right){//在搜索某个数时需要在两者相同时再搜索一次,避免漏掉两者相同时位置的这个数。

    int mid = left + (right-left)/2

    if(number[mid] == target)

        return mid;//找到符合要求的数后返回即可,没必要继续搜下去了

    else if(number[mid] > target)

        right = mid-1;//数过大,收缩右边界(因刚开始设置为右侧边界包含,所以需要减1)

    else if(number[mid] < target)

        left = mid+1;//数过小,收缩左边界(因刚开始设置为左侧边界包含,所以需要加1)

}

    return -1;//返回-1,代表在这个范围内搜不到想要的数

下面将结合力扣周赛题目讲解下如何应用。

题目是这样的:

 首先对题目进行分析可知:

(1)当糖果总数小于小孩个数时,我们是没办法分糖果的。所以这种情况应该考虑排除,思路也很简单,先计算糖果的总数,如果糖果总数小于小孩个数则直接返回0。

(2)当糖果总数大于等于小孩个数时,我们需要考虑最大化每个小孩能够得到的糖果数,那么在理想情况下,每个小孩能得到的糖果数最多必然是——糖果总数/小孩个数。但我们从题目中可以看到,这种情况的发生毕竟还是太过理想,毕竟并不是每堆糖果的数量都会大于平均数,那怎么办呢?很明显,我们需要在[1,糖果总数/小孩个数]的范围内找到最大的数,暂且记为n,使得candies中每个数/n的和能够大于k,这也就转化为二分搜索中的右侧边界搜索问题

那么解法也就随之而来了:

class Solution {

public:

    int maximumCandies(vector<int>& candies, long long k) {

        long long sum = 0;// 用来记录糖果总数

        int len = candies.size();

        //遍历得到糖果总数
        for(int i=0; i<len; ++i){

            sum += candies[i];

        }

        // 糖果总数小于小孩个数直接返回0,别犹豫。
        if(sum < k){

            return 0;

        }

        // 范围为[1,糖果总数/小孩数],我们需要转化一下,转为左闭右开的范围,也就是[1,糖果总数/小孩数+1)
        int left = 1, right = sum / k + 1;

        while(left < right){// 循环条件不包含相等的情况,因为我们定义的是左闭右开

            int mid = left + (right - left)/2;// 计算中间值

            long long number = 0;// 计算根据当前mid值能够将糖果分为多少堆,每个堆含有mid个糖果

            for(int i=0; i<len; ++i){

                number += candies[i] / mid;

            }

            if(number > k){// 说明mid值过小,虽然够均等分给k个小孩,但是小孩是贪心的呀,需要更多,所以继续搜索

                left = mid+1;

            }else if(number < k){// 说明mid值过大,不够均等分给k个小孩,分配不均容易引起小孩吵闹

                right = mid;

            }else{// 说明mid值正好能够均等分给k个小孩,但是不要着急返回呀,我们还要继续搜索,寻找有没有可能存在比当前值更大的数也能均等分给k个小孩,所以收缩左侧边界去继续搜索右侧边界。

                left = mid+1;

            }

        }

        return left-1; //返回右侧边界right-1=left-1,因为刚开始定义为左闭右开,所以最后返回右侧值right-1。

    }

};

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

二分搜索算法(以力扣周赛“每个小孩最多分到多少糖果”为例) 的相关文章

随机推荐