每日一題 - 劍指 Offer 53 - I. 在排序陣列中查詢數字 I

小鏘同學、發表於2020-07-05

題目資訊

  • 時間: 2019-07-04

  • 題目連結:Leetcode

  • tag:二分查詢 雜湊表

  • 難易程度:簡單

  • 題目描述:

    統計一個數字在排序陣列中出現的次數。

示例1:

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

示例2:

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

注意

1. 0 <= 陣列長度 <= 50000

解題思路

本題難點

排序陣列中查詢數字,效能最優。

具體思路

排序陣列中的搜尋問題,首先想到 二分法 解決。

排序陣列 nums 中的所有數字 target 形成一個視窗,記視窗的 左 / 右邊界 索引分別為 left 和 right ,分別對應視窗左邊 / 右邊的首個元素。

統計數字 target 的出現次數,可轉化為:使用二分法分別找到 左邊界 left 和 右邊界 right ,易得數字 target 的數量為 right−left−1 。

  • 計算中點 m=(i+j)/2(向下取整)
  • 若 nums[m]<target ,則 target 在閉區間 [m+1,j] 中,因此執行 i=m+1;
  • 若 nums[m]>target ,則 target 在閉區間 [i,m−1] 中,因此執行 j=m−1;
  • 若 nums[m]=target ,則右邊界 right 在閉區間 [m+1,j] 中;左邊界 left 在閉區間 [i,m−1] 中。
    • 若查詢 右邊界 right ,則執行 i=m+1 ;(跳出時 i指向右邊界)
    • 若查詢 左邊界 left ,則執行 j=m−1 ;(跳出時 j指向左邊界)

提示:查詢完右邊界後,可用 nums[j]=j 判斷陣列中是否包含 target ,若不包含則直接提前返回 0 ,無需後續查詢左邊界。查詢完右邊界後,左邊界 left一定在閉區間 [0,j] 中,因此直接從此區間開始二分查詢即可。

程式碼

class Solution {
    public int search(int[] nums, int target) {
        // 搜尋右邊界 right
        int i = 0, j = nums.length - 1;
        while(i <= j) {
            int m = (i + j) / 2;
            if(nums[m] <= target) i = m + 1;
            else j = m - 1;
        }
        int right = i;
        // 若陣列中無 target ,則提前返回
        if(j >= 0 && nums[j] != target) return 0;
        // 搜尋左邊界 right
        i = 0; 
        while(i <= j) {
            int m = (i + j) / 2;
            if(nums[m] < target) i = m + 1;
            else j = m - 1;
        }
        int left = j;
        return right - left - 1;
    }
}

複雜度分析:

  • 時間複雜度 O(logN) : 二分法為對數級別複雜度。
  • 空間複雜度 O(1) :幾個變數使用常數大小的額外空間。

其他優秀解答

解題思路

直接遍歷排序陣列,計數。

程式碼

class Solution {
    public int search(int[] nums, int target) {
        int count = 0;
        for(int num : nums){
            if(num == target){
              count++;
            }
        }
        return count;
    }
}

相關文章