1. 程式人生 > 其它 >【Leetcode】34. 在排序陣列中查詢元素的第一個和最後一個位置 Medium

【Leetcode】34. 在排序陣列中查詢元素的第一個和最後一個位置 Medium

 

 

本題並不困難, 但是引發了我對二分查詢演算法的一些思考。

二分查詢演算法的實現思想有兩種:

思路 1:在迴圈體中查詢元素   

迴圈內部有三個分支:

    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;
        while (left <= right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) return
mid; else if (nums[mid] < target) left = mid + 1; else right = mid - 1; } return -1; }

思路 2:在迴圈體中排除目標元素一定不存在的區間

迴圈內部有兩個分支:

    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;
        while (left < right)
        {
            
int mid = left + (right - left) / 2; if (nums[mid] < target) left = mid + 1; else right = mid; } if (nums[left] == target) return left; return -1; }
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1
; while (left < right) { int mid = left + (right - left + 1)/2; if (nums[mid] > target) right = mid - 1; else left = mid; } if (nums[left] == target) return left; return -1; }

排除法這裡有很多需要思考, 比如迴圈終止條件、mid需不需要向上取整。 總結一下編碼要點編碼要點
迴圈終止條件寫成:while (left < right) ,表示退出迴圈的時候只剩下一個元素;
在迴圈體內考慮如何縮減待搜尋區間,也可以認為是在待搜尋區間裡排除一定不存在目標元素的區間;
根據中間數被分到左邊和右邊區間,來調整取中間數的行為;
如何縮小待搜尋區間,一個有效的辦法是:從 nums[mid] 滿足什麼條件的時候一定不是目標元素去考慮,進而考慮 mid 的左邊元素和右邊元素哪一邊可能存在目標元素。一個結論是:當看到 left = mid 的時候,取中間數需要上取整,這一點是為了避免死迴圈;
退出迴圈的時候,根據題意看是否需要單獨判斷最後剩下的那個數是不是目標元素。
邊界設定的兩種寫法:

right = mid 和 left = mid + 1 和 int mid = left + (right - left) / 2; 一定是配對出現的;
right = mid - 1 和 left = mid 和 int mid = left + (right - left + 1) / 2; 一定是配對出現的。
這一點不需要記憶,只要一直思考目標元素可能存在的區間就可以了。因為下一輪搜尋的區間是 [left, mid] 所以這個時候設定 right = mid 。當前這條性質不滿足的時候,既然整個區間是 [left, right] 區間裡,第一種情況所在區間是 [left, mid] ,那麼另外一種情況對應的區間是 [mid + 1, right] ,兩個區間合起來就是整個區間 [left, right] 。同理,去理解 right = mid - 1 和 left = mid 這兩個邊界設定。

 

回到原題, 需要使用排除法找出target值的上下邊界

class Solution 
{
public:
    vector<int> searchRange(vector<int>& nums, int target) 
    {
        if (!nums.size()) return vector<int>{-1, -1};
        return vector<int> {searchLowerBound(nums, target),
                            searchUpperBound(nums, target)};
    }

    int searchLowerBound(vector<int>& nums, int target) 
    {
        int left = 0, right = nums.size() - 1;
        while (left < right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) left = mid + 1;
            else                    right = mid;
        }
        if (nums[left] != target) return -1;
        return left;
    }

    int searchUpperBound(vector<int>& nums, int target) 
    {
        int left = 0, right = nums.size() - 1;
        while (left < right)
        {
            int mid = left + (right - left + 1) / 2;
            if (nums[mid] > target) right = mid - 1;
            else                    left = mid;
        }
        if (nums[right] != target) return -1;
        return right;
    }
};