剑指offer34

174 阅读2分钟

题目描述

统计一个数字k在排序数组中出现的次数。

解题思路分析

  • 首先,如果数组的长度不是很大的话,遍历的时间复杂度为O(n),但是,这肯定是不行的
  • 我们发现这是一个有序数组,很容易就想到二分查找,而且时间复杂度为O(log2n)
  • 那么我们如何来使用二分查找找出k出现的个数,其实我们只要找出第一个k出现的位置和最后一个k出现的位置,然后就可以计算出k在此排序数组中
  • 那么我们如何计算出第一个和最后一个k出现的位置呢,其实也很简单,利用二分法,当我们查找到k时,检验其前面是不是k,如果是的话,说明这不是第一个k,那么第一个k一定在数组的前半段,递归查找下去,同样的,查找最后一个k时,我们找到一个k时,先检验这个k后面是不是k,如果是的话,那么这个k也不是最后一个k,最后一个k肯定在数组的后半段,这样就可以查找出第一个k和最后一个k出现的位置了

代码实现

public int getNumberOfK(int[] nums, int k) {
    if (nums == null || nums.length <= 0) {
        return 0;
    }
    int length = nums.length;
    int first = getFirstK(nums, 0, length - 1, k);
    int last = getLastK(nums, 0, length - 1, k, length);
    return (first != -1) && (last != -1) ? last - first + 1 : 0;
}
//查找第一个k
private int getFirstK(int[] nums, int start, int end, int k) {
    while (start <= end) {
        int mid = (start + end) >> 1;
        if (nums[mid] == k) {
            //如果mid前面那个数不是k或者mid已经是第一个数了,那么mid就是第一个k的位置了
            if (((mid - 1) > 0 && nums[mid - 1] != k) || mid == 0) {
                return mid;
            } else {
                end = mid - 1;
            }
        } else if (nums[mid] > k) {
            end = mid - 1;
        } else {
            start = mid + 1;
        }
    }
    return -1;
}
//查找最后一个k
private int getLastK(int[] nums, int start, int end, int k, int length) {
    while (start <= end) {
        int mid = (start + end) >> 1;
        if (nums[mid] == k) {
            //如果这个mid后面的那个数不是k或者mid已经到了最后一位,那么mid就是最后一个k的位置
            if (((mid + 1) < length && nums[mid + 1] != k) || mid == length - 1) {
                return mid;
            } else {
                start = mid + 1;
            }
        } else if (nums[mid] < k) {
            start = mid + 1;
        } else {
            end = mid - 1;
        }
    }
    return -1;
}