【代码随想录 | day01】(JavaScript)704. 二分查找,27. 移除元素

704.二分查找

何为二分法?

二分法,也称为折半法,是一种在有序数组中查找特定元素的搜索算法。

复杂度如何?

二分法的时间复杂度是O (logn)

所以在算法中,比O (n)更优的时间复杂度几乎只能是O (logn)的二分法。 根据时间复杂度来倒推算法也是面试中的常用策略:题目中若要求算法的时间复杂度是O (logn),那么这个算法基本上就是二分法。

算法时间复杂的的一个排行如下所示

O(1)常数阶 < O(logn)对数阶 < O(n)线性阶 < O(n^2)平方阶 < O(n^3)立方阶 < O(2^n)指数阶

算法思路?

二分法查找的思路如下:

  1. 首先,从数组的中间元素开始搜索,如果该元素正好是目标元素,则搜索过程结束,否则执行下一步。
  2. 如果目标元素大于/小于中间元素,则在数组大于/小于中间元素的那一半区域查找,然后重复步骤 1 的操作。
  3. 如果某一步数组为空,则表示找不到目标元素。

通用格式

注意

  • 是从“有序”数组里面寻找
  • 左右区间一般都是“左闭右闭”或者“左闭右开”。
  • 但是二分查找真正的坑根本就不是那个细节问题,而是在于到底要给 mid 加一还是减一,while 里到底用 <= 还是 <。(参考:二分查找算法细节详解,顺便写了首诗
采取“左闭右闭”的写法

采取“左闭右闭”的写法

var search = function(nums, target) {
    // 左闭右闭
    let left = 0;
    let right = nums.length - 1;
    while (left <= right) {
      let mid = (left + right) >> 1;  // 关注点
      if (nums[mid] == target) {
        return mid
      } else if (nums[mid] > target) {
        right = mid - 1;
      } else if (nums[mid] < target) {
        left = mid + 1;
      }
    }
    return -1
};
采取“左闭右开”的写法
var search = function(nums, target) {
    // 左闭右开
    var left = 0;
    var right = nums.length;
    while(left < right) {
        var mid = (left + right) >> 1;
        if(nums[mid] == target) {
            return mid
        } else if (nums[mid] > target) {
            right = mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        }
    }
};

总结 & 注意点

在这里插入图片描述

JavaScript 中 的变量 是松散类型 的,可以保存任何类型数据,变量只不过是一个名称。

所以当出现left=0,right=5时,直接使用(left + right) / 2得到的就是2.5 。

那么当值为undefined时,下列的if或者else if中条件都会不符合,这样left和right的值都不会发生变化,由此,就会一直在while中死循环。

27. 移除元素

想要移除数组中的元素,看似简单,实则很多细节需要我们去注意!

数组地址

像 C/C++ 这种传统的编译型的语言,它们的数组在内存中用一串连续的区域来存放一些值,而且它们的数组中存放的数据类型都需要预先设定成同一类型。

而对于JS来说的话,数组中存储的数据类型是可以完全不一致的,这就意味着,JS 数组中内存地址不是连续的。不过,现在的 JS 引擎为了优化 JS 的性能,它会分配一个连续的内存空间给存储了相同数据类型的数组,以达到更好的遍历效果。所以,只要你数组里存的是相同类型的值,在内存中的地址还是连续的

内存地址连续性
C/C++
JavaScript
存放的数据类型相同
地址连续
存放的数据类型相同
地址连续
存放的数据类型不同
地址不连续

js数组相关知识点

JavaScript删除数组中某一元素的方法:首先获取指定元素在数组中的位置(即索引index);然后使用splice()函数根据索引值来删除数组中的元素,语法格式splice(index, 1)

推荐阅读:

暴力解法

var removeElement = function (nums, val) {
  let len = nums.length;
    for(let i = 0; i < len; i++) {
        if(nums[i] == val) {
            nums.splice(i,1)
            // i--;  // 注意这里
        }
  }
  return nums
};
let nums = [0, 1, 2, 2, 3, 0, 4, 2], val = 2;
console.log(removeElement(nums, val));

错误想法
一开始想的的是使用for循环遍历数组中的每一个元素,用splice方法,把数组中和val的值给删除掉。但是遇到的问题是,let nums = [0, 1, 2, 2, 3, 0, 4, 2], val = 2;两个连续的2,当使用splice方法删除第一个2(元素下标为2)时,原数组中元素下标都会前移一位。第二个2的下标就会由3变成了2,但是此时继续进行下一轮遍历了,i=3,就会忽略元素中的第二个2 。

后续的解决方法就是,nums.splice(i,1);i--;不过这样的写法内存消耗大。

当然,还有一种双重for循环的解法,大家可以看看Carl老师关于此点的讲解

双指针解法

双指针算法的时间复杂度O(n)

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

fast指针用来遍历每一个元素,去确定这个元素能否留下来。如果这个元素不是需要删除的元素,那么就用慢指针去更新一下数组的内容。

想要了解的更透彻,可以看看Carl老师的视频讲解


day01 over!明天继续加油,先把后续需要学的内容放在这里~

关于链表!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值