38 - 数字在排序数组中的次数

题目描述:
统计一个数字在排序数组中出现的次数。
如输入排序数组 {1,2,3,3,3,3,4,5} 和数字3,由于 3 在数组中出现了 4 次,因此输出 4.


解析:
有序数组中查找一个数可以联想到二分查找
如例子中,要查找3的次数,二分查找,找到3后,它的左右两边可能都有3,因此两边都要查找,如果3在数组中出现了n次,则时间复杂度O(n),二分查找没有起到作用。

假设我们要在数组中查找 k 的次数。因为数组是有序的,那么只要找到数组中的第一个 k 和最后一个 k 就能够确定出现的次数。

  • 那么怎样才能确定是第一个 k 呢?
    二分查找 nums[mid] == k 时,且 mid 的前一项不为 k 。如果前一项仍为 k ,则继续用二分查找向前一半查找第一个 k。
  • 怎样才能确定是最后一个 k 呢?
    二分查找 nums[mid] == k 时,且 mid 的后一项不为 k 。如果后一项仍为 k ,则继续用二分查找向后一半查找最后一个 k。

我们不必查找到每一个 k ,只需查找 2 个 k ,因此时间复杂度 O(logn)

#include <iostream>
using namespace std;
int GetFirstK(int nums[], int start, int end, int k) {
    if (nums == NULL || start > end)
        return -1;
    int mid = (start + end) >> 1;
    int firstK = -1;
    if (nums[mid] == k) {
        if (mid == start || nums[mid-1] != k) // nums[mid] == k, 且 它的前一个不等于 k ,说明是第一个 k
            firstK = mid;
        else
            firstK = GetFirstK(nums, start, mid-1, k);
    } else if (nums[mid] > k) {
        firstK = GetFirstK(nums, start, mid-1, k);
    } else {
        firstK = GetFirstK(nums, mid+1, end, k);
    }
    return firstK;
}
int GetLastK(int nums[], int start, int end, int k) {
    if (nums == NULL || start > end)
        return -1;
    int mid = (start + end) >> 1;
    int lastK = -1;
    if (nums[mid] == k) {
        if (mid == end || nums[mid+1] != k)
            lastK = mid;
        else
            lastK = GetLastK(nums, mid+1, end, k);
    } else if (nums[mid] < k) {
        lastK = GetLastK(nums, mid+1, end, k);
    } else {
        lastK = GetLastK(nums, start, mid-1, k);
    }
    return lastK;
}
int GetCountsOfK(int nums[], int start, int end, int k) {
    if (nums == NULL || start > end)
        return 0;
    int firstK = GetFirstK(nums, start, end, k);
    int lastK = GetLastK(nums, start, end, k);
    if (firstK > -1 && lastK > -1)
        return lastK - firstK + 1;
    return 0;
}
int main() {
    int nums[] = {1,2,3,4,4,4,4,4,5,5,6,9};
    int k = 4;
    cout << GetCountsOfK(nums, 0, sizeof(nums)/sizeof(nums[0])-1, k) << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值