題目描述:給定一個按照升序排列的整數數組 nums,和一個目標值 target。找出給定目標值在數組中的開始位置和結束位置。
你的算法時間複雜度必須是 O(log n) 級別。
如果數組中不存在目標值,返回 [-1, -1]。
示例 1:
輸入: nums = [5,7,7,8,8,10], target = 8
輸出: [3,4]
示例 2:
輸入: nums = [5,7,7,8,8,10], target = 6
輸出: [-1,-1]
方法一:
線性掃描法
首先,我們對 nums 數組從左到右做線性遍歷,當遇到 target 時中止。如果我們沒有中止過,那麼 target 不存在,我們可以返回“錯誤代碼” [-1, -1] 。如果我們找到了有效的左端點座標,我們可以坐第二遍線性掃描,但這次從右往左進行。這一次,第一個遇到的 target 將是最右邊的一個(因爲最左邊的一個存在,所以一定會有一個最右邊的 target)。我們接下來只需要返回這兩個座標。
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] targetRange = {-1, -1};
for (int i = 0; i < nums.length; i++) {
if (nums[i] == target) {
targetRange[0] = i;
break;
}
}
if (targetRange[0] == -1) {
return targetRange;
}
for (int j = nums.length-1; j >= 0; j--) {
if (nums[j] == target) {
targetRange[1] = j;
break;
}
}
return targetRange;
}
}
方法二:
二分查找法
算法工作過程與線性掃描方法類似,除了找最左和最右下標的方法。這裏我們僅僅做幾個微小的調整,用這種修改過的二分查找方法去搜索這個排過序的數組。首先,爲了找到最左邊(或者最右邊)包含 target 的下標(而不是找到的話就返回 true ),所以算法在我們找到一個 target 後不能馬上停止。我們需要繼續搜索,直到 lo == hi 且它們在某個 target 值處下標相同。
另一個改變是 left 參數的引入,它是一個 boolean 類型的變量,指示我們在遇到 target == nums[mid] 時應該做什麼。如果 left 爲 true ,那麼我們遞歸查詢左區間,否則遞歸右區間。考慮如果我們在下標爲 i 處遇到了 target ,最左邊的 target 一定不會出現在下標大於 i 的位置,所以我們永遠不需要考慮右子區間。當求最右下標時,道理同樣適用。
class Solution{
private int extremeInsertionIndex(int [] nums,int target,boolean left)
{
int lo=0;
int hi=nums.length;
while(lo<hi)
{
int mid=(lo+hi)/2;
if(nums[mid]>target||(left&&target==nums[mid]))
{
hi=mid;
}
else
{
lo=mid+1;
}
}
return lo;
}
public int [] searchRange(int [] nums,int target)
{
int [] targetRange={-1,-1};
int leftIdx=extremeInsertionIndex(nums,target,true);
if(leftIdx==nums.length||nums[leftIdx]!=target)
return targetRange;
targetRange[0]=leftIdx;
targetRange[1]=extremeInsertionIndex(nums,target,false)-1;
return targetRange;
}
}
以上的算法使用c語言都很容易實現
對於二分查找法,很多人可能遇到過很多問題,比較mid的邊界問題,本博主在看了這篇二分查找算法細節詳解之後,大有感悟。所以推薦推薦。