代码随想录算法训练营001| 704.二分查找,26. 移除元素

博客围绕LeetCode的704.二分查找和27.移除元素两道题展开。二分查找介绍了左闭右闭和左闭右开两种区间定义及对应代码;移除元素给出暴力解法、快慢指针、相向双指针法等多种解题思路及代码,强调数组删除是覆盖操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

704. 二分查找

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

解题思路

二分查找的区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在 while 寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则
写二分法,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。

1. 左闭右闭即[left, right]

代码
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function (nums, target) {
  // 定义target在左闭右闭的区间里,即:[left, right]
  let left = 0; // 左指针
  let right = nums.length - 1; //右指针

  while (left <= right) {
    // 当left==right,区间[left, right]依然有效,所以用 <=
    let middle = (left + right) >> 1; // 防止溢出 等同于(left + right)/2
    if (nums[middle] > target) {
      right = middle - 1; // target 在左区间,所以[left, middle - 1]
    } else if (nums[middle] < target) {
      left = middle + 1; // target 在右区间,所以[middle + 1, right]
    } else {
      // nums[middle] == target
      return middle; // 数组中找到目标值,直接返回下标
    }
  }
  // 未找到目标值
  return -1;
};

2. 左闭右开即[left, right)

代码
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function (nums, target) {
  // 定义target在左闭右开的区间里,即:[left, right)
  let left = 0; // 左指针
  let right = nums.length; // 右指针

  while (left < right) {
    // 当left==right,在区间[left, right)是无效的空间,所以用 <
    let middle = (left + right) >> 1; // 防止溢出 等同于(left + right)/2
    if (nums[middle] > target) {
      right = middle; // target 在左区间,所以[left, middle)
    } else if (nums[middle] < target) {
      left = middle + 1; // target 在右区间,所以[middle + 1, right)
    } else {
      // nums[middle] == target
      return middle; // 数组中找到目标值,直接返回下标
    }
  }
  // 未找到目标值
  return -1;
};

27. 移除元素

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

解题思路

要知道数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。这里数组的删除并不是真的删除,只是将删除的元素移动到数组后面的空间内,然后返回数组实际剩余的元素个数,OJ 最终判断题目的时候会读取数组剩余个数的元素进行输出。

1. 暴力解法

两层 for 循环,一个 for 循环遍历数组元素,找到要删除的元素之后,第二个 for 循环更新数组。

代码
/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function (nums, val) {
  let len = nums.length;
  for (let i = 0; i < len; i++) {
    // 找到了要删除的元素
    if (nums[i] === val) {
      //  // 发现需要移除的元素,就将数组集体向前移动一位
      for (let j = i + 1; j < len; j++) {
        nums[j - 1] = nums[j];
      }
      i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
      len--; // 此时数组的大小-1
    }
  }
  return len;
};

2. 快慢指针

通过一个快指针和慢指针在一个 for 循环下完成两个 for 循环的工作。
a. 创建一个双指针,fastIndex指向当前将要处理的元素,slowIndex指向下一个将要赋值的位置。
b. 若nums[fastIndex]val不等,它一定是输出数组的一个元素,我们就将nums[slowIndex]改为nums[fastIndex],然后将slowIndexfastIndex指针同时右移。
c. 最后返回slowIndex即可;

代码
/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function (nums, val) {
  if (!nums.length) return 0;

  let slowIndex = 0;
  for (let fastIndex = 0; fastIndex < nums.length; fastIndex++) {
    if (nums[fastIndex] !== val) {
      nums[slowIndex++] = nums[fastIndex];
    }
  }
  return slowIndex;
};

3. 相向的双指针法

利用双指针,将右边不等于 val 的元素覆盖左边等于 val 的元素。

代码
/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function (nums, val) {
  if (!nums.length) return 0;

  let left = 0,
    right = nums.length - 1;

  while (left <= right) {
    // 比较val和左指针指向的元素,找左边等于val的元素
    while (left <= right && nums[left] !== val) {
      left++;
    }

    // 比较val和右指针指向的元素,找右边不等于val的元素
    while (left <= right && nums[right] === val) {
      right--;
    }

    // 将右边不等于val的元素覆盖左边等于val的元素
    if (left < right) {
      nums[left++] = nums[right--];
    }
  }
  // left一定指向了最终数组末尾的下一个元素
  return left;
};

4. 相向的双指针法

a. 利用双指针,将 nums 中等于 val 的元素移动到数组末尾;
b. 当 left=right 时, 如果 nums[left]等于 val, 即删除 nums[left], 返回 left 的值即可;如果 nums[left]不等于 val, 即不删除 nums[left], 返回 left+1

代码
/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function (nums, val) {
  if (!nums.length) return 0;

  let left = 0,
    right = nums.length - 1;

  while (left < right) {
    // 比较val和左指针指向的元素
    while (left < right && nums[left] !== val) {
      left++;
    }

    // 比较val和右指针指向的元素
    while (left < right && nums[right] === val) {
      right--;
    }
    // 跳出内部循环时, nums[left] === val、nums[right] !== val
    // 交换nums[left]和nums[right],此时数组尾部全是值为val的元素
    [nums[left], nums[right]] = [nums[right], nums[left]];
  }

  // 判断left===right时,nums[left]和val的关系
  return nums[left] === val ? left : left + 1;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只前端小菜鸟~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值