解題思路
經典的二分法查找,通過題目解析可知查找的元素位置爲:查找中間值大於等於目標值的第一個元素索引
參考[1]
//java
class Solution
{
public int searchInsert(int[] nums, int target)
{
int len = nums.length;
if (nums[len - 1] < target)
{
return len;
}
int left = 0;
int right = len - 1;
while (left <= right)
{
int mid = (left + right) / 2;
// 等於的情況最簡單,我們應該放在第 1 個分支進行判斷
if (nums[mid] == target)
{
return mid;
}
else if (nums[mid] < target)
{
// 題目要我們返回大於或者等於目標值的第 1 個數的索引
// 此時 mid 一定不是所求的左邊界,
// 此時左邊界更新爲 mid + 1
left = mid + 1;
}
else
{
// 既然不會等於,此時 nums[mid] > target
// mid 也一定不是所求的右邊界
// 此時右邊界更新爲 mid - 1
right = mid - 1;
}
}
// 注意:一定得返回左邊界 left,
// 如果返回右邊界 right 提交代碼不會通過
// 【注意】下面我嘗試說明一下理由,如果你不太理解下面我說的,那是我表達的問題
// 但我建議你不要糾結這個問題,因爲我將要介紹的二分查找法模板,可以避免對返回 left 和 right 的討論
// 理由是對於 [1,3,5,6],target = 2,返回大於等於 target 的第 1 個數的索引,此時應該返回 1
// 在上面的 while (left <= right) 退出循環以後,right < left,right = 0 ,left = 1
// 根據題意應該返回 left,
// 如果題目要求你返回小於等於 target 的所有數裏最大的那個索引值,應該返回 right
return left;
}
}
算法複雜度分析:
時間複雜度:O(logN) 原因是數組長度N每次劃分爲一半,2^x=N 求解x可得出
空間複雜度:O(1)
使用二分法模板:
二分查找的改進以及相關的題型
把if (nums[mid] == target) 的語句省略,首先第一步判斷目標值是否能包含在左右邊界之中,找出左右邊界。若不能包含在左右邊界之中,最後通過二分法排除只剩下的最後一個元素(夾逼法)還要做最後一次判斷,以判斷目標值是否和最後剩下的值相等。
第二步是 依據題目寫出中位數排除的判斷分支 left=mid+1
第三步是 對應的中位數不能排除的分支 right=mid
或者第二步和第三步顛倒過來
第二步 right=mid-1 第三步 left=mid
第四步返回left或者right即可。
注意偶數時:左中位數和右中位數的區分選擇(通過最後剩下兩個元素時判斷)。以避免死循環
//java
class Solution
{
public int searchInsert(int[] nums, int target)
{
int len = nums.length;
if (len == 0)
{
return -1;
}
if (nums[len - 1] < target)
{
return len;
}
int left = 0;
int right = len - 1;
//排除上述特殊情況後,依據題目可以確定目標值一定在在左右邊界之中
while (left < right)
{
int mid = left + (right - left) / 2;
if (nums[mid] < target) //依據題目排除中位數(此判斷中位數小於目標值,而題目要找的是大於或等於目標值的第一個元素)
{
// nums[mid] 的值可以捨棄
left = mid + 1;
}
else //中位數大於或等於目標值
{
// nums[mid] 不能捨棄
right = mid;
}
}
//循環結束只剩下最後一個值
return right;
}
}
總結該模板和普通二分法的差別在於:
1)去掉了中間值的判斷等於,新的模板在最後會得到最後一個數
2)在該模板中需要確定要找的元素是否包含在邊界內(left right)若不在則最後得到的元素要再做一次判斷
3)新模板最後返回的left=right
4)排除中位數的寫法有兩種,兩種寫法要注意的問題是:左中位數和右中位數的選擇不同的寫法不同,注意不然容易溢出
參考資料