代码随想录算法训练营第一天 | LeetCode 704.二分查找 27.移除元素

237 阅读2分钟

题目链接:leetcode.cn/problems/binary-search/description/ https://leetcode.c…

文章讲解:programmercarl.com/0704.%E4%BA… programmercarl.com/0027.%E7%A7…

704.二分查找

首先,注意使用条件:

  1. 不重复
  2. 升序

此外,为了避免溢出,left + right 可以用 left + (right-left)代替。

二分查找本身算法思路很简单,初中就学过:找中间值,然后与目标值比大小,收缩单侧区间。

1.左闭右闭

难点在于思考清楚:left = middle + 1;到底要不要加这个1(right的-1同理);以及, right = nums.length - 1要不要减这个1

当前nums[middle]已经不是满足条件的值了,而我们采用的是闭区间,那就意味着下次循环的两端值都要能取到,所以就不能再直接用middle值了,故而左右需要分别+1和-1。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    // 左闭右闭区间 [left, right]
    let left = 0, right = nums.length - 1;
    while(left <= right) {
        // 右移一位位运算是除以2,防止相加时溢出
        let middle = left + ((right - left) >> 1); 
        if (nums[middle] < target) {
            left = middle + 1;
        } else if (nums[middle] > target) {
            right = middle - 1;
        } else {
            return middle;
        }   
    }
    return -1;
};

2.左闭右开

右边开区间,就意味着右边值取不到,所以right = nums.length & while (left < right) & right = middle

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    // 左闭右开 [left, right)
    let left = 0, right = nums.length
    while (left < right) {
        let middle = left + ((right - left) >> 1);
        if (nums[middle] < target) {
            left = middle + 1;
        } else if (nums[middle] > target) {
            right = middle;
        } else {
            return middle;
        }
    }
    return -1;

};

27.移除元素

做这道题需要理解数组的结构,是连续存储,删除元素的本质是用后面的元素覆盖这个元素。

1.暴力双层循环

这种方法思路简单,但有一些细节需要注意。

外层循环用于遍历元素,内层循环对符合条件的元素进行后覆盖前的操作。 注意内层循环的索引j,如果让j=i的话,j+1实际就超过了i。所以,让j = i+1, 然后nums[j - 1] = nums[j]

另外,所有后面的元素都往前移动了一位,所以需要i--,从当前元素位置开始继续向后遍历。如果没有i--,则可能漏掉后一个元素。比如[3, 3, 1, 2], val:3,可能就会漏掉第二个3。

/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function(nums, val) {
    let length = nums.length;
    for(let i = 0; i < length; i++) {
        if (nums[i] === val) {
            for (let j = i + 1; j < length; j++) {
                nums[j - 1] = nums[j]
            }
            i--;
            length--;
        }
        
    }
    return length;
};

2.同向双指针(快慢指针)

这种算法难在思路:即用一次循环,两个快慢指针,在原数组上构建出一个不含目标元素的新数组。 slow是新数组的下标,所以如果不是val,就需要将其加入这个新数组,并且slow++。

其实核心就是,想象用slow构建出一个全新的数组

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

// 双指针(快慢指针法)
var removeElement = function(nums, val) {
    let size = nums.length;
    let slow = 0; // 新数组
    for (let fast = 0; fast < size; fast++) {
        if (nums[fast] !== val) {
            nums[slow] = nums[fast];
            slow++;
        }
    }
    return slow;
};