35. Search Insert Position 搜索插入位置 #Binary Search 新的二分法算法

35. Search Insert Position

https://leetcode.com/problems/search-insert-position/

Description

Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.

You may assume no duplicates in the array.

Example 1:

Input: [1,3,5,6], 5
Output: 2
Example 2:

Input: [1,3,5,6], 2
Output: 1
Example 3:

Input: [1,3,5,6], 7
Output: 4
Example 4:

Input: [1,3,5,6], 0
Output: 0

Solutions

  1. 题目的意思是:从左到右遍历,返回第一个>=目标值target的数组内索引(=是因为按题意:如果有相同的数,就插在原有的数的前面~)
  • 特殊情况:如果目标值(严格)大于排序数组的最后一个数,返回这个排序数组的长度
  1. 二分法

题目告诉你“排序数组”,其实就是在疯狂暗示你用二分查找法。

本题需要:
二分查找到等于target的那个数,返回此数的数组内索引
(如果不存在那个数,就需要找到相邻两个数,target介于这个相邻两数之间,返回两树中较大的那个的索引)。
参考这篇文章特别好用的二分查找法模板,学习怎么写二分法。

Submissions

我的~

class Solution {
    public int searchInsert(int[] nums, int target) {
        int len = nums.length;
        if (nums[len - 1] < target) {
            return len;
        }
        
        int l = 0;
        int r = len - 1;
        
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(nums[mid]==target)
                return mid;
            if(nums[mid]<target)
                l=mid+1;
            if(nums[mid]>target)
                r=mid-1;
        }
        //总感觉有点绕,可以自己断点看看过程
        return l;
    }
}

传统二分查找法的问题在于,当数组中不存在与target相等的数的时候,退出 while 循环后,应该返回左边界还是右边界比较容易出错。

流程是这样的,当数组中不存在与target相等的数的时候,
一定会走到l=r这一步,
如果l=r这里的数>target,就应该插入到l=r这个位置,但是r会--,所以要返回l;
如果l=r这里的数<target,就应该插入到l+1或r+1这个位置,l会++,所以要返回l。

这里这篇文章提出了一个新的算法思路,不过我感觉之前的二分算法也是可以用的,只不过是根据不同的题,判断一下不同的情况。

二分查找法之所以高效,是因为它利用了数组有序的特点,在每一次的搜索过程中,都可以排除将近一半的数,使得搜索区间越来越小,直到区间成为一个数(索引)

当数组有偶数个元素时,中位数有,一种左中位数,一种右中位数,如何选择左右是本算法的关键!(不然会出现死循环,死循环就容易发生在区间元素只有 2 个时候,此时中位数的选择尤为关键。)
那么,什么时候使用左中位数,什么时候使用右中位数呢?

  1. 先编写分支的逻辑循序先写“排除逻辑”所在的分支。
    即先考虑能把“中位数”排除在外的逻辑,而不能排除“中位数”的逻辑放在 else 分支里。
  2. 根据以下规则选择:
  • 1、如果分支的逻辑,在选择左边界的时候,不能排除中位数,那么中位数就选“右中位数”,只有这样区间才会收缩,否则进入死循环;
  • 2、同理,如果分支的逻辑,在选择右边界的时候,不能排除中位数,那么中位数就选“左中位数”,只有这样区间才会收缩,否则进入死循环。
public class Solution {
    public int searchInsert(int[] nums, int target) {
        int len = nums.length;
        //先对边界情况进行判断这样比较快
        if (len == 0) {
            return 0;
        }
        if (target > nums[len - 1]) {
            return len;
        }
        
        int left = 0;
        int right = len - 1;
        while (left < right) //退出循环时,左边界等于右边界,因此你不必纠结要返回 l 还是 r ,此时返回 l 或者 r 都是可以的
        {
            //这里的mid有两种,一种左中位数,一种右中位数,是本算法的关键!!!
            //这题中选择的是左中位数
            //右中位数是:
            //int mid = left + (right - left + 1) / 2;
            int mid = left + (right - left) / 2;//这样的写法可以避免溢出
            if (nums[mid] < target) {
                left = mid + 1;//左边界不包含答案,可以先排除
            } else {
                right = mid;
            }
        }
        return right;
    }
}

上面是先写了左边界,因为此题中左边界好判断,可以先排除掉,所以(这是个规则,不是逻辑推理)中位数选左中位数mid = left + (right - left) / 2
同样的,如右边界好判断,则选右中位数mid = left + (right - left + 1) / 2

Summary

这个算法看了两天,绕死我了……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值