1. 程式人生 > 實用技巧 >35. 搜尋插入位置(C++)

35. 搜尋插入位置(C++)

目錄

題目

給定一個排序陣列和一個目標值,在陣列中找到目標值,並返回其索引。如果目標值不存在於陣列中,返回它將會被按順序插入的位置。

你可以假設陣列中無重複元素。

示例 1:

輸入: [1,3,5,6], 5
輸出: 2

示例 2:

輸入: [1,3,5,6], 2
輸出: 1

示例 3:

輸入: [1,3,5,6], 7
輸出: 4

示例 4:

輸入: [1,3,5,6], 0
輸出: 0

分析與題解

暴力遍歷

我們直接遍歷給定排序陣列nums中查詢val值,大致會出現三種情況:

  • 陣列中找到目標元素,因為陣列中不存在重複元素
    ,直接返回目標元素的對應下標即可
  • 陣列中找不到目標元素,且存在元素大於目標值,首位大於目標值的下標就是插入的目標下標
  • 陣列中找不到目標元素,且所有元素都小於目標值,直接將目標值插入到陣列的末尾nums.size()

經過上述討論,我們可以將第二、三情況判斷條件歸結到一起:對於排序陣列,只要當nums[i] >= val,我們就可以返回當前下標。

完整程式碼如下:

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int num = nums.size();
        for (int i = 0; i < num; i++) {
            if (nums[i] >= target)
                return i;
        }
        // 退出迴圈後則直接在陣列末尾插入元素
        return num;
    }
};

二分查詢(左閉右開)

注意右區間為開區間,所以初始化左右邊界時,右邊界可以賦值為陣列的長度

實際上該下標是越界的,無法訪問到

int left = 0, right = nums.size();

然後求取中值時取左中值,並且需要注意防止溢位:

int mid = left + (right - left) / 2;

對於中值下標我們與目標值進行比較:

  • nums[mid] == target,我們直接返回中值下標
  • nums[mid] < target哪怕target不在陣列中,需要進行插入也不可能插入到當前位置,我們直接向後移位。那麼target值可能的取值區間變為[mid + 1, right)
  • nums[mid] > target,此時不確定中值下標對應元素是否為首位大於目標值的元素,該下標有可能成為插入元素下標,需要進行保留。那麼target值可能的取值區間變為[left, mid)

完整程式碼如下:

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left = 0, right = nums.size();
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target)
                return mid;
            else if (nums[mid] < target)
                left = mid + 1;
            else if (nums[mid] > target)
                right = mid;
        }
        return left;
    }
};

二分查詢(左閉右閉)

注意討論的區間為左閉右閉區間,所有右邊界的初始下標必須是確實能夠進行訪問的:

int left = 0, right = num - 1;

關於中值的計算方式類似,但是因為是閉區間,無論nums[mid]target關係如何,都需要對中值mid進行加(減)一操作。

因為雙閉區間的迴圈判斷條件為while (left <= right),所以退出while迴圈後左右邊界的關係為left = right + 1,總共可分為四種情況進行討論:

  • 當陣列中存在目標元素,即nums[mid] == target,那麼迴圈中就可以返回目標下標
  • 當陣列中不存在目標元素:
    • 當目標元素大於陣列中所有元素,此時迴圈時右邊界不會改變,左邊界會一直向右移動,退出迴圈的區間為[nums.size() , nums.size() - 1]
    • 當目標元素小於陣列中所有元素,此時迴圈時左邊界不會改變,右邊界會一直向左移動,退出迴圈的區間為[0, -1]
    • 當目標元素在取值在陣列所有元素之間時,此時迴圈的左/右邊界都會改變,退出迴圈的區間為[targetIndex, targetIndex - 1]

綜上討論,退出迴圈後關於插入元素下標的選擇,要麼選擇left,要麼選擇right + 1

完整程式碼如下:

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int num = nums.size();
        int left = 0, right = num - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target)
                return mid;
            else if (nums[mid] < target)
                left = mid + 1;
            else if (nums[mid] > target)
                right = mid - 1;
        }
        return right + 1;
    }
};