日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

每天一道LeetCode-----某个数在递增序列第一次和最后一次出现的位置

發布時間:2024/4/19 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 每天一道LeetCode-----某个数在递增序列第一次和最后一次出现的位置 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Search for a Range

原題鏈接Search for a Range

給定一個遞增序列和一個值,找到該值在序列中出現的范圍,實際上就是找到該值第一次出現和最后一次出現的位置。如果沒有,返回[-1,-1]


遞增序列肯定是二分了,正常二分法查找算法如下,是通過判斷中間位置的值與給定值的大小關系,從而將區間變為原來的一半,繼續查找,不斷的一半,一半,最后變成只有一個元素的區間,比較后返回。

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

普通的二分法查找到一個相等的值就結束了,但是這里需要確定這個值第一次出現和最后一次出現的位置。所以很明顯不能讓它結束這么快,也就是說即使nums[middle] == target,也不返回,因為目的是要找一個范圍,即兩個邊界,而middle只是一個點,不一定是邊界,有可能middle前面和后面也都是等于target的位置。
但是又因為二分法最后肯定會收斂到一個點,不能直接找范圍,所以可以先找左邊界,再找右邊界


二分法需要保證,如果序列中存在目標元素target,那么最后收斂到的位置的值一定是target

對于左邊界
考慮二分法的實現,每次找到middle后比較nums[middle]和target的大小關系

  • 如果nums[middle] > target,說明target在[left, middle)區間,改變right = middle - 1;
  • 如果nums[middle] < target,說明target在(middle, right]區間,改變left = middle + 1;
  • 如果nums[middle] == target,說明第一次出現target的位置在[left, middle]內,改變right = middle。因為middle位置有可能就是第一次出現target的位置,所以不能讓right = middle - 1;

對于右邊界
考慮二分法的實現,每次找到middle后比較nums[middle]和target的大小關系

  • 如果nums[middle] > target,說明target在[left, middle)區間,改變right = middle - 1;
  • 如果nums[middle] < target,說明target在(middle, right]區間,改變left = middle + 1;
  • 如果nums[middle] == target,說明最后一次出現target的位置在[middle, right]內,改變left = middle。因為middle位置有可能就是最后一次出現target的位置,所以不能讓right = middle - 1;

但是!考慮一種情況,求右邊界時,某次區間[left, right]長度只有2,也就是說right = left + 1,這就導致middle = left。如果nums[middle] == target,根據上面的式子,另left = middle,此時left根本沒有變化,也就是說改變left后區間根本沒有更新,會陷入無限循環

這種問題只出現在求右邊界的情況,原因是在求左邊界時,right不可能和middle相等,所以每次區間都會變小,不會出現上面的問題

怎么解決呢,可以當區間長度為2時手動判斷nums[left]和nums[right]。因為目的是求最后一個target出現的位置,而right的位置是區間的最右邊,所以如果nums[right] == target,那么根本就不需要再找了,right就是最后一次出現target的位置 ,而如果nums[right] != target,那么right -= 1將right左移。

代碼如下

class Solution { public:vector<int> searchRange(vector<int>& nums, int target) {if(nums.empty())return {-1, -1};int front = equalLeftBound(nums, target);int back = equalRightBound(nums, target);if(front < nums.size() && back >= 0 && nums[front] == target && nums[back] == target)return {front, back};elsereturn {-1, -1};} private:/* 尋找第一次出現target的位置 */int equalLeftBound(vector<int>& nums, int target){int left = 0;int right = nums.size() - 1;while(left < right){int middle = (left + right) / 2;/** if(nums[middle] < target)* left = middle + 1;* else if(nums[middle] > target)* right = middle - 1;* else* right = middle;*//* * 這里將nums[middle] > target和nums[middle] == target合在一起* 對于left是否需要加一可以通過nums[middle]是否等于target判斷* 因為right永遠不會和middle相等,所以區間會一直減小,不會出現無限循環* 怎么寫無所無,但是右邊界不行*/if(nums[middle] < target)left = middle + 1;elseright = middle;/* * 如果nums[middle] < target導致left = middle + 1后* nums[left]仍然小于target會導致left繼續加一,這里可能出現left > right的情況* 如果此時right = nums.size() - 1,那么left就越界了* 返回的left也就越界了,需要在返回后判斷*/if(nums[left] == target)break;else++left;}return left;}/* 尋找最后一次出現target的位置 */int equalRightBound(vector<int>& nums, int target){int left = 0;int right = nums.size() - 1;while(left < right){/* * 這里只能合在一起,因為left可能和middle相等,導致區間根本沒有更新,導致無限循環* 所以需要改變區間,從而將區間縮小*/int middle = (left + right) / 2;if(nums[middle] > target)right = middle - 1;elseleft = middle;/* 如果右邊界就是target,直接返回即可,否則需要將right減小,因為最后的結果是right *//* * 同理左邊界,如果nums[middle] > target導致right = middle - 1* 而nums[right]仍然大于target,會導致right繼續減一,可能出現right < left的情況* 如果此時left = 0,那么right就越界了,需要在返回后判斷是否越界*/if(nums[right] == target)break;else--right;}return right;} };

總結

以上是生活随笔為你收集整理的每天一道LeetCode-----某个数在递增序列第一次和最后一次出现的位置的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。