Swift 数据结构与算法(11 ) 数组 + Leetcode35. 搜索插入位置

55 阅读1分钟

题目

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

 

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2

示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1

示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4

 

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 为 无重复元素 的 升序 排列数组

解题思路🙋🏻‍ ♀️

使用数组 nums = [1, 3, 5, 7, 9, 11] 和目标值 target = 8 进行演示 searchInsert 函数的执行过程。

  • 首先,我们初始化左边界 L = 0 和右边界 R = nums.count - 1 = 5

  • 然后,我们开始 while 循环,条件为 L <= R

第一次循环:

  • 我们计算 mid 的值:mid = L + (R - L) / 2 = 0 + (5 - 0) / 2 = 2。因此,mid 的值为 2

  • 我们发现 nums[mid] = 5 小于目标值 8,所以我们将 L 更新为 mid + 1 = 3

数组:[1,3,5,7,9,11]
      L  M     R
索引:  0  1  2 3 4  5

第二次循环:

  • 我们重新计算 mid 的值:mid = L + (R - L) / 2 = 3 + (5 - 3) / 2 = 4。因此,mid 的值为 4

  • 我们发现 nums[mid] = 9 大于目标值 8,所以我们将 R 更新为 mid - 1 = 3

数组:[1,3,5,7,9,11]
            LMR
索引:  0  1  2 3 4  5

第三次循环:

  • 我们重新计算 mid 的值:mid = L + (R - L) / 2 = 3 + (3 - 3) / 2 = 3。因此,mid 的值为 3

  • 我们发现 nums[mid] = 7 小于目标值 8,所以我们将 L 更新为 mid + 1 = 4

数组:[1,3,5,7,9,11]
               L R
索引:  0  1  2 3 4  5

现在,我们的左边界 L = 4,右边界 R = 3,不满足 while 循环的条件 L <= R,所以循环结束。

最后,我们返回 L 的值 4,表示目标值 8 应该插入的位置。

边界思考🤔

代码

第一版.

class Solution {
    func searchInsert(_ nums: [Int], _ target: Int) -> Int {
        // 初始化左右边界
        var left = 0
        var right = nums.count - 1

        // 当左边界小于右边界时,进行二分查找
        while left < right {
            // 计算中间值
            let mid = left + (right - left) / 2
            
            // 如果中间值大于目标值,更新右边界为中间值减一
            if nums[mid] > target {
                right = mid - 1
            // 如果中间值小于目标值,更新左边界为中间值加一
            } else if nums[mid] < target {
                left = mid + 1
            // 如果中间值等于目标值,直接返回中间值索引
            } else {
                return mid
            }
        }

        // 二分查找结束,如果左边界对应的值大于目标值,返回左边界
        if nums[left] > target {
            return left
        // 如果左边界对应的值小于目标值,返回左边界加一
        } else if nums[left] < target {
            return left + 1
        // 如果左边界对应的值等于目标值,返回左边界
        } else {
            return left
        }
    }
}

第二版本

class Solution {
    func searchInsert(_ nums: [Int], _ target: Int) -> Int {
        var left = 0
        var right = nums.count - 1

        while left <= right {
            let mid = left + (right - left) / 2
            if nums[mid] < target {
                left = mid + 1
            } else {
                right = mid - 1
            }
        }

        return left
    }
}

注意

区别在这一行

while left <= right {

while left < right {

shipWithinDays 中的船的最低运载能力, (10 的题目))。我们希望找到的是满足某种条件的最小值(或最大值)。在这种情况下,我们通常使用 left < right 作为循环条件。

在循环结束时,leftright 将指向我们要找的值。在 shipWithinDays 的问题中,leftright 最终会指向满足所有货物在给定天数内运送完成的最小运载能力。

选择 < 还是 <= 主要取决于我们的目标。如果我们正在寻找一个特定的值,我们通常使用 <=。如果我们正在寻找一个满足特定条件的最小值或最大值,我们通常使用 <

时空复杂度分析

O(logn)