【二分法】LeetCode-Search Insert Position

前言

今天在LeetCode遇到一个这样的题目.

SearchInsertPosition@2x.png

题目意思就是给一个排好序的数组和要寻找的数,若数组存在,返回它的index,否则返回它该插入的位置。

思考

拿到这个问题,哇,这不就是普通的二分法吗?那就刷刷的写下了二分法的代码:

func searchInsert(_ nums: [Int], _ target: Int) -> Int {
    var low = 0
    var high = nums.count - 1
    var mid = -1
    while low <= high {
        mid = (high + low) / 2
        if target == nums[mid] {
            return mid
        }else if target > nums[mid] {
            low = mid + 1
        }else {
            high = mid - 1
        }
    }
    return -1
}

以上代码是最原始的二分查找法,若存在返回对应的索引,不存在返回-1。而题目额外要求找不到,就返回要插入的位置。这时候我就纠结low,mid,high之间,究竟哪一个才是要插入的位置呢?想了半天没思绪,看了下答案,只需要将return -1改为return low就可以了。

疑惑

为什么是返回low,而不是返回high,或者是返回mid。所以我又搜了一下关于二分法的相关资料,看完这篇博客后,恍然大悟。

二分法有很多变种形式,例如查找最后一个等于或者小于key的元素等形式(可以去看该博客)。最后该博客,总结了二分法各形式变化,以下为该博客的结论:

1、首先确定返回的是left,还是right

  • 跳出while (left <= right)循环条件是right < left,且right = left - 1。
  • right和left一定是卡在”边界值”的左右两边
  • 如果是比较值为key,查找小于等于(或者是小于)key的元素,则边界值就是等于key的所有元素的最左边那个,其实应该返回left。

例子

  以数组{1, 2, 3, 3, 4, 5}为例,如果需要查找第一个等于或者小于3的元素下标,我们比较的key值是3,则最后left和right需要满足以下条件:

例子.png

我们比较的key值是3,所以此时我们需要返回left

2、判断出比较符号

int mid = (left + right) / 2;
if (array[mid] ? key) {
    //... right = xxx;
}
else {
    // ... left = xxx;
}

也就是这里的 if (array[mid] ? key) 中的判断符号,结合步骤1和给出的条件,如果是查找小于等于key的元素,则知道应该使用判断符号>=,因为是要返回left,所以如果array[mid]等于或者大于key,就应该使用>=,以下是完整代码

// 查找小于等于key的元素
int mid = (left + right) / 2;
if (array[mid] >= key) {
    right = mid - 1;
}
else {
    left = mid + 1;
}

为什么返回的是low

套用结论,high = low - 1

  • target小于所有元素,插入的位置是0,high为-1,low=0才能跳出循环
  • target大于所有元素,插入的位置的数组的长度count,high为count-1,low=count才能跳出循环
  • target小于数组中某些元素,大于某些元素,问题就转化为查找第一个大于target的元素下标。根据结论,high和low总是在临界值的两边,那么就相当于 high-target-low,因此返回low

总上述三种情况,返回low都符合题目要求

总结

看似简单的二分法,如果换种提问方式,或许就绕进去出不来了。因此,在这里做这个总结,加深自己的理解

猜你喜欢

转载自blog.csdn.net/bloody_leaves/article/details/78362087