数字在排序数组中出现的次数

数字在排序数组中出现的次数

http://www.nowcoder.com/questionTerminal/70610bf967994b22bb1c26f9ae901fa2

题目的主要信息:
  • 给定一个长度为n的非降序数组和一个数字k,求k在数组中出现的次数
举一反三:

学习完本题的思路你可以解决如下题目:

JZ11. 旋转数组的最小数字

方法:二分法(推荐使用)

知识点:分治

分治即“分而治之”,“分”指的是将一个大而复杂的问题划分成多个性质相同但是规模更小的子问题,子问题继续按照这样划分,直到问题可以被轻易解决;“治”指的是将子问题单独进行处理。经过分治后的子问题,需要将解进行合并才能得到原问题的解,因此整个分治过程经常用递归来实现。

思路:

因为data是一个非降序数组,它是有序的,这种时候我们可能会想到用二分查找。但是一个数组可能有多个k,而且我们要查找的并非常规二分法中k出现的位置,而是k出现的左界和k出现的右界。要是能刚好找到恰好小于k的数字位置和恰好大于k的数字的位置就好了。

再有因为数组中全是整数,因此我们可以考虑,用二分查找找到k+0.5k+0.5应该出现的位置和k0.5k-0.5应该出现的位置,二者相减就是k出现的次数。

return bisearch(array, k + 0.5) - bisearch(array, k - 0.5);

具体做法:

  • step 1:写一个二分查找的函数在数组中找到某个元素出现的位置。每次检查区间中点值,根据与中点的大小比较,确定下一次的区间。
  • step 2:分别使用二分查找,找到k+0.5和k-0.5应该出现的位置,中间的部分就全是k,相减计算次数就可以了。

图示:

alt

Java实现代码:

public class Solution {
    //二分查找
    private int bisearch(int[] data, double k){ 
        int left = 0;
        int right = data.length - 1;
        //二分左右界
        while(left <= right){ 
            int mid = (left + right) / 2;
            if(data[mid] < k)
                left = mid + 1;
            else if(data[mid] > k)
                right = mid - 1;
        }
        return left;
    }
    public int GetNumberOfK(int [] array , int k) {
        //分别查找k+0.5和k-0.5应该出现的位置,中间的部分就全是k
        return bisearch(array, k + 0.5) - bisearch(array, k - 0.5);
    }
}

C++实现代码:

class Solution {
public:
    //二分查找
    int bisearch(vector<int>& data, float k){ 
        int left = 0;
        int right = data.size() - 1;
        //二分左右界
        while(left <= right){ 
            int mid = (left + right) / 2;
            if(data[mid] < k)
                left = mid + 1;
            else if(data[mid] > k)
                right = mid - 1;
        }
        return left;
    }
    int GetNumberOfK(vector<int> data ,int k) {
        //分别查找k+0.5和k-0.5应该出现的位置,中间的部分就全是k
        return bisearch(data, k + 0.5) - bisearch(data, k - 0.5);
    }
};

Python实现代码:

class Solution:
    #二分查找
    def bisearch(self, data: List[int], k: float) -> int:
        left = 0
        right = len(data) - 1
        #二分左右界
        while left <= right: 
            mid = (left + right) // 2
            if data[mid] < k:
                left = mid + 1
            elif data[mid] > k:
                right = mid - 1
        return left
    
    def GetNumberOfK(self , data: List[int], k: int) -> int:
        #分别查找k+0.5和k-0.5应该出现的位置,中间的部分就全是k
        return self.bisearch(data, k + 0.5) - self.bisearch(data, k - 0.5)

复杂度分析:

  • 时间复杂度:O(log2n)O(log_2n),其中nn为数组长度,两次二分查找,二分查找复杂度为O(log2n)O(log_2n)
  • 空间复杂度:O(1)O(1),常数级变量,无额外辅助空间
全部评论
上界下界注释写反了叭
19 回复 分享
发布于 2020-06-20 20:25
直接写左边界有边界,不好吗???
2 回复 分享
发布于 2020-07-06 21:43
写反了 而且 lbound = r;吧
1 回复 分享
发布于 2020-07-01 16:26
为什么这里r = nums.size(); 而不是 nums.size()-1 有大佬解释下吗
1 回复 分享
发布于 2020-12-14 10:12
官方题解有点坑啊
2 回复 分享
发布于 2020-09-15 17:14
为什么mid=l+(r-1)/2啊?mid不是应该等于(l+r)/2的吗?
2 回复 分享
发布于 2020-11-08 20:22
二分法改成python版本耗时太长,根本通不过,大家不要被官方题解误导了!
点赞 回复 分享
发布于 2020-08-03 22:52
注释的上下界反了吧
点赞 回复 分享
发布于 2021-01-28 10:38
上下界写反了呢
点赞 回复 分享
发布于 2021-08-22 21:08
自己实现lower_bound(),需要注意初始 r=nums.size(), 而不是 r=nums.size()-1。写错的话,当遇到很大的数,只会返回最后的下标,但实际上应该返回最后的下标+1
点赞 回复 分享
发布于 2022-03-15 11:36
官方题解的思路真的很有意思。刚开始意味需要两个方法,分别求左边界和右边界,原来各加减0.5就可以做到,只要求左边界即可。推荐大家看一下五点七边的视频,有助于理解“循环不变量”。
点赞 回复 分享
发布于 2023-04-04 12:18 江苏
`mid_search`这个函数的结构是通用结构,但少了等于k时的跳出操作,所以最后得到的l一定是对应值的右边界+1。主要讲传入的参数和return为什么是两次的差值。 因此如果找的是`k-0.5`,那么这一次的l对应的值就是第一次出现k的下标。举个例子好理解,如果`k=3,` 则`k-0.5=2.5`, 那么2.5的右侧一位就是3。 需要注意`k+0.5`有点不一样,`k+0.5=3.5`,所以右边一位是4,不是3。所以这一段**返回的l是目标值k范围的右边界+1**,而**k-0.5返回的l是目标值k范围的左边界**。因此最后直接相减就可以了。
点赞 回复 分享
发布于 2023-08-04 11:41 江西

相关推荐

蚂蚁 基架java (n+6)*16 签字费若干
点赞 评论 收藏
分享
评论
36
5
分享
牛客网
牛客企业服务