• 统计目标成绩的出现次数(数字在排序数组中出现次数),剑指offer,力扣


    目录

    我们直接看题解吧:

    审题目+事例+提示:

    思路(二分法,双边):

    代码:

    优化:

    代码:

    直接一次二分:


    力扣题址: 

    LCR 172. 统计目标成绩的出现次数 - 力扣(LeetCode)


    今天刷统计目标成绩的出现次数(数字在排序数组中出现次数),大家有兴趣可以点上看看题目要求,试着做一下

    我们直接看题解吧:

    看到题目可能首先想到直接暴力循环,遍历数组,

    当然,也有人可能会想到用哈希表

    但以上方法都没有利用题目的关键条件,那就是排序的数组,在做排序数组问题时首先想到的应该是二分查找

    因此方法适合用二分法(单边或双边)

    审题目+事例+提示:

    数组是非严格递增(允许重复元素)排序数组

    思路(二分法,双边):

    关键:利用target首次出现与最后出现的下标差

    1、初始化,左边界i=0,右边界j=length-1

    ​​​​​ 2、循环二分:当闭区间[i,j]无元素跳出

               a、计算中点m=i+(j-i)/2(向下取整)

                b、Scores[m]>target,则i=m+1

                c、cores[m]

               d、Scores[m]==target

              ·右边界right在[m+1,j],查找右边界则i=m+1(i指向右边界)

          若查找右边界后,用scores[m]==target判断数组是否含target,无返回0,有则遍历左边界。

          此时左边界一定在闭区间[0,j]中

             ·  左边界right在[i,m-1],查找左边界则j=m-1(指向左边界)

       3、返回值:应用二分查找right和left,最终返回right-left-1

    这里分享一个取中点的小知识:

       假设 low与high分别在数组左右两边,则中点=low+(high-low)

    (这种写法看起来有点脱裤子放屁的意思,但)它主要好处是防止(high+low)过大时造成溢出

    代码:

    1. class Solution {
    2. public int countTarget(int[] scores, int target) {
    3. // 搜索右边界 right
    4. int i = 0, j = scores.length - 1;
    5. while(i <= j) {
    6. int m = (i + j) / 2;
    7. if(scores[m] <= target) i = m + 1;
    8. else j = m - 1;
    9. }
    10. int right = i;
    11. // 若数组中无 target ,则提前返回
    12. if(j >= 0 && scores[j] != target) return 0;
    13. // 搜索左边界 right
    14. i = 0; j = scores.length - 1;
    15. while(i <= j) {
    16. int m = (i + j) / 2;
    17. if(scores[m] < target) i = m + 1;
    18. else j = m - 1;
    19. }
    20. int left = j;
    21. return right - left - 1;
    22. }
    23. }

    优化:

    由于上面出现了两次二分查找,我们可以提高代码的复用性,将查找右边界right封装在一个函数中,而target 是整数,所以利用target 与target-1的下标之差

    代码:

    1. class Solution {
    2. public int countTarget(int[] scores, int target) {
    3. return helper(scores, target) - helper(scores, target - 1);
    4. }
    5. int helper(int[] scores, int tar) {
    6. int i = 0, j = scores.length - 1;
    7. while(i <= j) {
    8. int m = i+(j-i) / 2;
    9. if(scores[m] <= tar) i = m + 1;
    10. else j = m - 1;
    11. }
    12. return i;
    13. }
    14. }

     评论区大佬的注释:

    1、 while(i <= j),这里不可用是i < j ,设想int[] nums = {1,2,3},target=3,第一次i = 0,j = 2;第二次 i = 1,j = 2;第三次 i = 2,j = 2。如果是while(i < j),而第三次是i = j ,不满足 i < j 的条件,退出while循环,会导致无法判断 i = j 的情况,不知道nums[i]是否等于target,就直接返回。

    2、第一个if语句: if(nums[m] <= tar) ,这里因为合并了nums[m] < tar和nums[m] = tar两种情况,所以一开始看的十分头晕,建议为了可读性还是分开写。主要说 nums[m] = tar,因为helper方法是求右边界的!所以只要nums[m] = tar,则 i = m + 1,直到nums[m] 不等于 tar,此时就找到了tar的右边界。所以只要返回(tar的右边界减去tar-1的右边界),即可得到tar出现的次数。(这也说明为什么nums[m] = tar时,还需要 i = m +1,因为这是为了【找右边界】,与二分查找略有不同,二分查找是nums[m] = tar,就返回m了)

    3、假如tar-1不存在呢?比如{1,2,3,5,7,7,7,8},tar=7,而tar-1=6并不在数组nums里边。回看第二个问题if(nums[m] <= tar)合并了两个条件, 上边说了nums[m] = tar,这里说说nums[m] < tar。其实tar-1存在与否并不重要,按照开头的数组,设tar=6。第一次i = 0,j = 7,m=3,nums[m]=5 tar;第三次i = 4,j = 4,m = 4,nums[m] = 7 >tar;第四次 i = 4,j = 3,i>j 退出while,renturn 4,tar的右边界为下标4。为什么tar不存在不影响?我展示一下拙见,如有合适的解释,欢迎大佬补充。因为helper方法是为了求右边界,tar不存在时,不考虑nums[m] == tar的判断;nums[m] > tar,j = m-1;nums[m] < tar,i = m+1,最终返回的是tar的右边界(return i 保证是返回右边界,不可用return j)。所以tar的右边界一定存在,即使tar不存在

    直接一次二分查找:

    (不过在某些极端情况会出现时间复杂度退化)

    1. class Solution {
    2. public int search(int[] nums, int target) {
    3. int low = 0, high = nums.length - 1;
    4. while (low<=high){
    5. //防止整数过大而溢出
    6. int mid=low+(high-low)/2;
    7. if(nums[mid]<target){
    8. low=mid+1;
    9. }else if(nums[mid]>target){
    10. high=mid-1;
    11. }else{
    12. if(nums[high]!=target)
    13. high--;
    14. else if(nums[low]!=target)
    15. low++;
    16. else
    17. break;
    18. }
    19. }
    20. //返回的要+1,因为是从数组下标开始
    21. return high-low+1;
    22. }
    23. }

  • 相关阅读:
    php如何实现文件上传
    【Leetcode】1177. Can Make Palindrome from Substring
    第十九章《类的加载与反射》第1节:类的加载、连接和初始化
    【Java SE】如何解读Java的继承和多态的特性?
    [思考进阶]01 如何克服自己的无知?
    [Ubuntu-20.04.x]让服务的启动命令放在/etc/rc.local文件中可以开机自启动
    java基本语法 上
    普中51单片机学习(DS1302)
    C/C++ 新生入学管理系统
    短视频引爆销售:TikTok如何改变跨境电商游戏规则
  • 原文地址:https://blog.csdn.net/m0_70437378/article/details/134314299