1. 程式人生 > >LeetCode-34. Search for a Range

LeetCode-34. Search for a Range

改變 必須 條件篩選 col sea 二分法 tro 例子 spa

一、問題描述

  給定一個升序排列的數組nums,和一個目標數target,找出nums中target的索引範圍。

  例子:給定數組為{5, 7, 7, 8, 8, 10},target=8。返回{3,4}

二、問題解決

  思路一:直接遍歷就可以,第一次碰到target的時候記錄位置1,繼續遍歷到不是target或數組尾,記錄下位置2。時間復雜度為n

vector<int> searchRange1(vector<int>& nums, int target) {
    vector<int> result = {-1,-1};
    for
(int i = 0; i < nums.size(); ++i) { if (nums[i] == target) { result[0] = i; while (i < nums.size() && nums[i] == target) i++; result[1] = i - 1; return result; } } return result; } int main() { vector
<int> v = { 5,7,7,8,8,10 }; vector<int> result = searchRange(v, 8); for (auto i : result) { cout << i << endl; } system("pause"); return 0; }

  思路二:使用二分查找,找到第一個target之後,向左找第一個不是target的位置或是數組頭,記錄為位置1。然後從第一個target位置向後尋找,找到第一個不是target的位置或是數組尾,記錄為位置2。這樣復雜度比第一種情況要低一些,但是和數組中和target值相等的元素個數有關系。

vector<int> searchRange(vector<int>& nums, int target) {
    vector<int> result = { -1,-1 };
    int pos1 = 0;
    int pos2 = nums.size() - 1;
    while (pos1 <= pos2) {
        int middle = (pos1 + pos2) / 2;
        if (nums[middle] == target) {
            int temp = middle;
            while (middle >= 0&&nums[middle] == target)
                --middle;
            result[0] = middle+1;
            while (temp < nums.size() && nums[temp] == target)
                ++temp;
            result[1] = temp - 1;
            return result;
        }
        if (nums[middle] > target)
            pos2 = middle - 1;
        if (nums[middle] < target)
            pos1 = middle + 1;
    }
    return result;
}

  思路三:使用徹底的二分查找思路,能保證復雜度降到logn。先使用二分法找出左邊節點,再找出又邊節點。但是找出左右節點需要對二分法做一些修改。

vector<int> searchRange(vector<int>& nums, int target) {
    //總體思路,先二分法找到左端點,再二分法找到又端點
    vector<int> result = {-1,-1};
    //註意這裏pos2初始化為size()而不是size()-1。
    int pos1 = 0; int pos2 = nums.size();
    int middle = (pos1 + pos2) / 2;
    while (pos1 < pos2) {
        middle = (pos1 + pos2) / 2;
        //註意此處和二分法的差別,等於條件的處理是怎麽樣的
        if (nums[middle] >= target)
            pos2 = middle ;
        if (nums[middle] < target)
            pos1 = middle + 1;
    }
    //第一個條件判斷是不是沒找到target,第二個條件篩選數組程度為1,且沒有找到的情況。
    if (nums.size() == pos1 || nums[pos1] != target)
        return result;
    result[0] = pos1;

    //當pos1=n,pos2=n+1的時候,middle是指向n的,要找右邊的節點,就必須想辦法改變一下二分法。
    pos1 = 0;
    pos2 = nums.size() ;
    middle = (pos1 + pos2) / 2;
    while (pos1 < pos2) {
        middle = (pos1 + pos2) / 2;
        if (nums[middle] > target)
            pos2 = middle;
        if (nums[middle] <= target)
            pos1 = middle +1;
    }
    //這裏需要減一
    result[1] = pos1-1;
    return result;
}

三、問題思考

  第三種方法需要復習,理解二分法。

  二分法需要註意的地方:

  1、while循環的判斷條件贏寫成while(pos1<=pos2)而不是while(pos1<pos2),沒有等號時,當數組只有一個元素的情況會漏。在pos1=n,pos2=n+1,且target=nums[pos2]的時候也會遺漏。

  2、當pos1=n,pos2=n+1時,middle會取得n,應為/2時忽略的余數,這使得選取middle會有“向左移”的傾向,思路三在找出右邊範圍的時候因此做了很多的修改。需要註意。

LeetCode-34. Search for a Range