每天一道LeetCode-----某个数在递增序列第一次和最后一次出现的位置
Search for a Range
原題鏈接Search for a Range
給定一個(gè)遞增序列和一個(gè)值,找到該值在序列中出現(xiàn)的范圍,實(shí)際上就是找到該值第一次出現(xiàn)和最后一次出現(xiàn)的位置。如果沒(méi)有,返回[-1,-1]
遞增序列肯定是二分了,正常二分法查找算法如下,是通過(guò)判斷中間位置的值與給定值的大小關(guān)系,從而將區(qū)間變?yōu)樵瓉?lái)的一半,繼續(xù)查找,不斷的一半,一半,最后變成只有一個(gè)元素的區(qū)間,比較后返回。
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; }普通的二分法查找到一個(gè)相等的值就結(jié)束了,但是這里需要確定這個(gè)值第一次出現(xiàn)和最后一次出現(xiàn)的位置。所以很明顯不能讓它結(jié)束這么快,也就是說(shuō)即使nums[middle] == target,也不返回,因?yàn)槟康氖且乙粋€(gè)范圍,即兩個(gè)邊界,而middle只是一個(gè)點(diǎn),不一定是邊界,有可能middle前面和后面也都是等于target的位置。
但是又因?yàn)槎址ㄗ詈罂隙〞?huì)收斂到一個(gè)點(diǎn),不能直接找范圍,所以可以先找左邊界,再找右邊界
二分法需要保證,如果序列中存在目標(biāo)元素target,那么最后收斂到的位置的值一定是target
對(duì)于左邊界
考慮二分法的實(shí)現(xiàn),每次找到middle后比較nums[middle]和target的大小關(guān)系
- 如果nums[middle] > target,說(shuō)明target在[left, middle)區(qū)間,改變r(jià)ight = middle - 1;
- 如果nums[middle] < target,說(shuō)明target在(middle, right]區(qū)間,改變left = middle + 1;
- 如果nums[middle] == target,說(shuō)明第一次出現(xiàn)target的位置在[left, middle]內(nèi),改變r(jià)ight = middle。因?yàn)閙iddle位置有可能就是第一次出現(xiàn)target的位置,所以不能讓right = middle - 1;
對(duì)于右邊界
考慮二分法的實(shí)現(xiàn),每次找到middle后比較nums[middle]和target的大小關(guān)系
- 如果nums[middle] > target,說(shuō)明target在[left, middle)區(qū)間,改變r(jià)ight = middle - 1;
- 如果nums[middle] < target,說(shuō)明target在(middle, right]區(qū)間,改變left = middle + 1;
- 如果nums[middle] == target,說(shuō)明最后一次出現(xiàn)target的位置在[middle, right]內(nèi),改變left = middle。因?yàn)閙iddle位置有可能就是最后一次出現(xiàn)target的位置,所以不能讓right = middle - 1;
但是!考慮一種情況,求右邊界時(shí),某次區(qū)間[left, right]長(zhǎng)度只有2,也就是說(shuō)right = left + 1,這就導(dǎo)致middle = left。如果nums[middle] == target,根據(jù)上面的式子,另left = middle,此時(shí)left根本沒(méi)有變化,也就是說(shuō)改變left后區(qū)間根本沒(méi)有更新,會(huì)陷入無(wú)限循環(huán)
這種問(wèn)題只出現(xiàn)在求右邊界的情況,原因是在求左邊界時(shí),right不可能和middle相等,所以每次區(qū)間都會(huì)變小,不會(huì)出現(xiàn)上面的問(wèn)題
怎么解決呢,可以當(dāng)區(qū)間長(zhǎng)度為2時(shí)手動(dòng)判斷nums[left]和nums[right]。因?yàn)槟康氖乔笞詈笠粋€(gè)target出現(xiàn)的位置,而right的位置是區(qū)間的最右邊,所以如果nums[right] == target,那么根本就不需要再找了,right就是最后一次出現(xiàn)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:/* 尋找第一次出現(xiàn)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合在一起* 對(duì)于left是否需要加一可以通過(guò)nums[middle]是否等于target判斷* 因?yàn)閞ight永遠(yuǎn)不會(huì)和middle相等,所以區(qū)間會(huì)一直減小,不會(huì)出現(xiàn)無(wú)限循環(huán)* 怎么寫(xiě)無(wú)所無(wú),但是右邊界不行*/if(nums[middle] < target)left = middle + 1;elseright = middle;/* * 如果nums[middle] < target導(dǎo)致left = middle + 1后* nums[left]仍然小于target會(huì)導(dǎo)致left繼續(xù)加一,這里可能出現(xiàn)left > right的情況* 如果此時(shí)right = nums.size() - 1,那么left就越界了* 返回的left也就越界了,需要在返回后判斷*/if(nums[left] == target)break;else++left;}return left;}/* 尋找最后一次出現(xiàn)target的位置 */int equalRightBound(vector<int>& nums, int target){int left = 0;int right = nums.size() - 1;while(left < right){/* * 這里只能合在一起,因?yàn)閘eft可能和middle相等,導(dǎo)致區(qū)間根本沒(méi)有更新,導(dǎo)致無(wú)限循環(huán)* 所以需要改變區(qū)間,從而將區(qū)間縮小*/int middle = (left + right) / 2;if(nums[middle] > target)right = middle - 1;elseleft = middle;/* 如果右邊界就是target,直接返回即可,否則需要將right減小,因?yàn)樽詈蟮慕Y(jié)果是right *//* * 同理左邊界,如果nums[middle] > target導(dǎo)致right = middle - 1* 而nums[right]仍然大于target,會(huì)導(dǎo)致right繼續(xù)減一,可能出現(xiàn)right < left的情況* 如果此時(shí)left = 0,那么right就越界了,需要在返回后判斷是否越界*/if(nums[right] == target)break;else--right;}return right;} };總結(jié)
以上是生活随笔為你收集整理的每天一道LeetCode-----某个数在递增序列第一次和最后一次出现的位置的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 每天一道LeetCode-----有序数
- 下一篇: 每天一道LeetCode-----寻找地