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

152 阅读3分钟

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. 如果目标元素大于/小于中间元素,则在数组大于/小于中间元素的那一半区域查找,然后重复步骤第一步的操作。
  3. 如果某一步数组为空,则表示找不到目标元素。 二分法查找的时间复杂度O(logn)。

注意

  • 是从“有序”数组里面寻找
  • 左右区间一般都是“左闭右闭”或者“左闭右开”。
  • 下标:left:0,right: arr.length-1
  • 但是二分查找真正的坑根本就不是那个细节问题,而是在于到底要给 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;
        }
    }
};

总结 & 注意点

mid=(left+right)>>1的含义

右移运算符>>,运算结果正好能对应一个整数的二分之一值,这就正好能代替数学上的除2运算,但是比除2运算要快。

mid=(left+right)>>1相当于mid=(left+right)/2

防溢出.png

运算符.png

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

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

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


27. 移除元素

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

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

image.png

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

错误思路:采用暴力解法的话

错误解法.png

一开始想的的是使用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--;len--;不过这样的写法内存消耗大。

后来想到把val值换成其他字符,然后删掉。首先考虑的是replace,但是!replace()方法可以实现替换字符串操作。不能对数组进行操作

 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--;
             len--;
         }
     }
     console.log(nums)
 };

参考文章: