剑指 Offer 53 - I. 在排序数组中查找数字 I

142 阅读1分钟

题目

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

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2

示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109

解析

由于题目给的nums 数组是递增且升序的, 因此可以找到 target第一次出现的位置first,和 target 最后一次出现位置last,则 target 出现的次数为 last - first + 1, 反之,如果没找到target, 出现次数为0

由于数组nums有序,查找target的可以使用二分查找,分别查找target的第一次出现索引first, 最后一次出现索引last

查找first的具体流程

  1. k = target 定义递归函数getFirstK(nums, k, start, end), 在start = 0, end = nums.length-1 的范围中查找K
  2. 如果start > end, 返回 -1, 说明没找到k
  3. 声明 midIndex = Math.floor((start + end) / 2)
  4. 如果 nums[midIndex] > k, 说明要缩小右边界end = midIndex -1
  5. 如果 nums[midIndex] < k, 说明要扩大左边界start = midIndex + 1
  6. 如果 nums[midIndex] == k, 此时要分为两种情况:
    • 如果nums[midIndex-1] != k, 说明中间数字的前一个不等于k, 此时midIndexk 第一次出现的位置, 直接返回
    • 如果 midIndex == 0, 说明中间数字为数组的第一个数字,不存在前一个数字,midIndexk 第一次出现的位置,直接返回
    • 以上都不是的,说明中间数字的前一个数字也等于k, 此时要缩小右边界,end = midIndex - 1
  7. 递归执行getFirstK(nums, k, start, end)

查找last的具体流程

  1. k = target 定义递归函数getLastK(nums, k, start, end), 在start = 0, end = nums.length-1 的范围中查找K
  2. 如果start > end, 返回 -1, 说明没找到k
  3. 声明 midIndex = Math.floor((start + end) / 2)
  4. 如果 nums[midIndex] > k, 说明要缩小右边界end = midIndex -1
  5. 如果 nums[midIndex] < k, 说明要扩大左边界start = midIndex + 1
  6. 如果 nums[midIndex] == k, 此时要分为两种情况:
    • 如果nums[midIndex+1] != k, 说明中间数字的后一个不等于k, 此时midIndexk 最后一次出现的位置, 直接返回
    • 如果 midIndex == nums.length - 1, 说明中间数字为数组的最后一个数字,不存在后一个数字,midIndexk 第一次出现的位置, 直接返回
    • 以上都不是的,说明中间数字的前一个数字也等于k, 此时要缩小右边界,start = midIndex + 1
  7. 递归执行getLastK(nums, k, start, end)

参考代码

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    // 第一次出现的位置
    const k = target;
    const first = getFirstK(nums, k, 0, nums.length - 1);
    // 最后一次出现的位置
    const last = getLastK(nums, k, 0, nums.length - 1);
    // 如果first和last都能找到
    if (first > -1 && last > -1) {
        return last - first + 1;
    }
    // 没找到返回 0
    return 0;

};

function getFirstK(nums, k, start, end) {
    if (start > end) {
        return -1;
    }

    let midIndex = Math.floor((start + end) / 2);

    if (nums[midIndex] > k) {
        end = midIndex - 1;
    } else if (nums[midIndex] < k) {
        start = midIndex + 1;
    } else {
        if (nums[midIndex - 1] != k || midIndex == 0) {
            return midIndex;
        } else {
            end = midIndex - 1;
        }
    }
    return getFirstK(nums, k, start, end);
}


function getLastK(nums, k, start, end) {
    if (start > end) {
        return -1;
    }

    let midIndex = Math.floor((start + end) / 2);

    if (nums[midIndex] > k) {
        end = midIndex - 1;
    } else if (nums[midIndex] < k) {
        start = midIndex + 1;
    } else {
        if (nums[midIndex + 1] != k || midIndex == nums.length - 1) {
            return midIndex;
        } else {
            start = midIndex + 1;
        }
    }
    return getLastK(nums, k, start, end);
}

原题链接

剑指 Offer 53 - I. 在排序数组中查找数字 I