【LeetCode】704. 二分查找(简单)——代码随想录算法训练营Day01

题目链接:

题目描述

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

提示:

你可以假设 nums 中的所有元素是不重复的。
n 将在 [1, 10000]之间。
nums 的每个元素都将在 [-9999, 9999]之间。

文章讲解:代码随想录

视频讲解:手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找_哔哩哔哩_bilibili

题解1:左闭右闭的二分查找

正如题目所言,数组是有序数组,且无重复元素,使用二分查找即可解决。而二分查找,区间的划分是非常重要的,区间的划分同时影响到了边界处理。

下面是经典的二分查找的写法,是一种左闭右闭([left, right])的二分查找,也是最普遍的写法。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    let left = 0, right = nums.length - 1;
    let mid;

    while (left <= right) {
        mid = (left + right) >> 1;
        if (nums[mid] === target) {
            return mid;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    return -1;
};

 分析:使用二分查找比双重循环速度要快,时间复杂度为 O(logn),空间复杂度为 O(1)。

题解2:左闭右开的二分查找

下面是另一种二分查找的写法,是左闭右开的二分查找。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    let left = 0, right = nums.length;

    while (left < right) {
        const mid = left + ((right - 1 - left) >> 1);
        if (nums[mid] === target) {
            return mid;
        } else if (nums[mid] > target) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    return -1;   
};

分析:左闭右开的二分查找,时间复杂度为 O(logn),空间复杂度为 O(1)。

同类题 35. 搜索插入位置(简单)

35. 搜索插入位置

直接用左闭右闭的二分查找解决。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var searchInsert = function(nums, target) {
    let left = 0, right = nums.length - 1;

    while (left <= right) {
        const mid = (left + right) >> 1;
        if (nums[mid] === target) {
            return mid;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    return left;
};

分析:左闭右闭的二分查找,时间复杂度为 O(logn),空间复杂度为 O(1)。

同类题 34. 在排序数组中查找元素的第一个和最后一个位置(中等)

34. 在排序数组中查找元素的第一个和最后一个位置

题解1:左右夹逼暴力求解

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var searchRange = function(nums, target) {
    let left = 0, right = nums.length - 1;

    while (left <= right) {
        let flag = false;
        if (nums[left] < target) {
            left++;
            flag = true;
        }
        if (nums[right] > target) {
            right--;
            flag = true;
        }
        if (!flag) {
            break;
        }
    }
    if (nums[left] !== target || nums[right] !== target) {
        return [-1, -1];
    }
    return [left, right];
};

分析:完整遍历整个数组,时间复杂度为 O(n),空间复杂度为 O(1)。

题解2:二分查找

思路:调用两次二分查找分别找出等于目标值的第1个位置和大于目标值的第1个位置,即可得出答案。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */

var searchRange = function(nums, target) {
    // flag 为 false 时找左,为 true 时找右
    const binarySearch = function (arr, flag) {
        let left = 0, right = arr.length - 1;
        let res = arr.length;
        while (left <= right) {
            const mid = left + ((right - left) >> 1);
            if (arr[mid] > target || !flag && arr[mid] >= target) {
                // 找左时向左边夹逼
                right = mid - 1;
                res = mid;
            } else {
                left = mid + 1;
            }
        }
        return res;
    };

    const start = binarySearch(nums, false);
    const end = binarySearch(nums, true) - 1;
    if (start > end) {
        return [-1, -1];
    }
    return [start, end];
};

分析:使用了两次二分查找找出目标位置,时间复杂度为 O(logn),空间复杂度为 O(1)。

收获

理解了二分查找的原理,掌握了左闭右闭和左闭右开的两种二分查找的写法,对边界处理也有了新的认知。

你可能感兴趣的:(代码随想录算法训练营,#,LeetCode,数组,算法,数组,代码随想录算法训练营)