704.二分查找

力扣题目链接:https://leetcode.cn/problems/binary-search/

题目描述:

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

示例:

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

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

思路:

二分法查找的思路是不断地将数组二分,判断target应当属于哪个部分。思路很简单,但是应当注意边界条件的选取和每次二分的规则。

闭区间的查找规则:定义三个指针low,middle,high分别指向当前查找范围的第一、中间、最后的元素。在[low,right]中查找,那么也就意味着low和high对应元素的值也可能是target,我们在查找中就不能将low和high随意舍去。

代码如下:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    let len = nums.length
    let low = 0
    let high = len-1
    let middle = Math.floor(high/2)

    while(low <= high){
        //target在低半部分
        if(target >= nums[low] && target < nums[middle]){
            //下面考虑了等于middle的值的情况,所以在这里要减1
            high = middle - 1
            middle = low + Math.floor((high - low) / 2)
        }
        //target在高半部分
        else if(target <= nums[high] && target > nums[middle]){
            low = middle + 1
            middle = low + Math.floor((high - low) / 2)
        }
        //target在中间
        else if(target === nums[middle]){
            return middle;
        }
        //不在数组中
        else{
            return -1;
        }
    }

};

还有一种左闭右开的方式,即查找的是[low,high),需要将high定义为数组最后一个元素的下标
+1,在更新时策略也有所不同,就不写了,理解了闭区间的思路这种方式也很好理解。

27.移除元素

力扣题目链接:https://leetcode.cn/problems/remove-element/

题目描述:

给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

思路:

看到这个题目的第一眼就想到了选择排序、插入排序的那种做法:使前k个元素有序,不断地将后面的元素放到前面这k个元素中。

需要两个指针low,high。low从前往后,high从后往前。在一次循环中,递增low直至找到不符合的元素,递减high直至找到符合条件的元素,将二者互换,使low之前全为符合条件的,high之后全为不符合条件的。本题可以不用互换,因为不用考虑超出新长度后面的元素。

代码如下:

/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function(nums, val) {
    let len = nums.length
    if (len === 0) return 0

    let low = 0
    let high = len-1
    
    while(1){        
        while(nums[low] !== val){
            if(low === len) return len //全都不用删除
            low++           
        }
        while(nums[high] === val){
            if(high === 0) return 0 //全要删除
            high--
        }
        if(low > high) break

        //交换,或者直接覆盖也行:nums[low] = nums[high]
        let tmp = nums[low]
        nums[low] = nums[high]
        nums[high] = tmp
    }

    return low
};