从算法层面记录JavaScript中的数组知识点(持续更新中...)

338 阅读6分钟

几乎所有的编程语言都原生支持数组类型,因为数组是最简单的内存数据结构。


斐波拉契数列

斐波拉契数列,又称为黄金分割数列,其规律是数列中的前俩项是1,从第三项开始,每一项都等于前俩项之和。
题目:求斐波那契数列的前20个数

const fibonacci = []; 
fibonacci[1] = 1;
fibonacci[2] = 1;
for(let i=3;i<=20;i++) {
    fibonacci[i] = fibonacci[i-2] + fibonacci[i-1]
}
for(let i =1; i<=20; i++) {
    console.log(fibonacci[i])
}

这里把斐波那契数列中的前俩个数分别付给了数组的第二个和第三个位置。因为在JavaScript中,数组第一位的索引始终是0,因为斐波那契数列中不存在0,所以这里直接略过,从第二位开始分别保存斐波拉契数列中对应位置的元素。

LeetCode相关题目:509.斐波那契数
斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,   F(1) = 1 
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

给定 N,计算 F(N)。 示例 1:

输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1.

示例 2:

输入:3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2.

示例 3:

输入:4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3.

提示:0 ≤ N ≤ 30

这个题目虽然简单,但是却涉及到俩个算法知识点。
解法一:暴力递归 这个方法比较消耗性能。

var fib = function(N) {
    if(N === 0) return 0;
    if(N === 1) return 1;
    return fib(N-1) + fib(N-2)
}

解法二:动态规划 思路是数组存放所有的值,返回最后一个即可。

/**
 * @param {number} N
 * @return {number}
 */
var fib = function(N) {
    const fibonaci = [];
    fibonaci[0] = 0;
    fibonaci[1] = 1;
    fibonaci[2] = 1;
   <!--const fibonaci = [0,1,1]-->
    for(i=3;i<=N;i++) {
        fibonaci[i]=fibonaci[i-1] +fibonaci[i-2];
    }
    return fibonaci[N];
};

解法三:动态规划优化
优化上述方法,只需要存放前俩个值即可,减少空间消耗。

var fib = function(N) {
    if(N==0) return 0;
    if(N===2) return 1for(let i=3; i<N; i++) {
        let sum = prev + curr;
        prev = curr;
        curr = sum;
    }
    return curr;
}

数组增删方法

添加元素

  • 在数组末尾插入元素

方法一:把值赋给数组中最后一个空位上元素
我们知道在JavaScript中,数组是一个可以修改的对象,如果添加元素,它就会动态增长。

let arr = [0, 1, 2, 3, 4, 5] ;
arr[arr.length] = 6; // 6 
//arr = [0, 1, 2, 3, 4, 5, 6]

方法二: push方法

let arr = [0, 1, 2, 3, 4, 5] ;
arr.push(6); // 7 (数组的length)
//arr = [0, 1, 2, 3, 4, 5, 6]

// 也可以push多个
arr.push(7, 8) // 9 (数组的length)
// arr= [0, 1, 2, 3, 4, 5, 6, 7, 8]
  • 在数组开头插入元素

方法一:首先腾出数组第一个元素的位置,然后把所有元素向右移动一个位置,把要插入的值放到第一个位置。
可以将该方法直接添加在Array的原型上,使所有数组的实例都可以访问到该方法。

let arr = [0, 1, 2, 3, 4, 5] ;
Array.prototype.insertFirstPostion = function(value) {
    for(let i=this.length; i>0; i--) {
        this[i] = this[i-1]
    }
    this[0] = value;
}
 arr.insertFirstPosition(-1); 
 // arr = [-1, 0, 1, 2, 3, 4, 5]

方法二:unshift方法

let arr = [0, 1, 2, 3, 4, 5] ;
arr.unshift(-1); // 7 (数组的length)
//arr = [-1, 0, 1, 2, 3, 4, 5]

// 也可以增加多个
arr.unshift(-3,-2); // 9 (数组的length)
// arr= [-3, -2, -1, 0, 1, 2, 3, 4, 5]

删除元素

  • 从数组末尾删除元素
    pop方法
let arr = [0, 1, 2, 3, 4, 5] ;
arr.pop(); // 5(数组长度length)
// arr = [0, 1, 2, 3, 4]
  • 从数组开头删除元素
    方法一:把数组中的所有元素左移一位,但是数组的长度不变,意味着数组中有额外的一个元素(值是undefined)。在最后一次循环里,i+1引用了数组里还未初始化的一个位置。在Java、C/C++、或C#里这样写会抛出一次,不得不在numbers.length-1处停止循环。
    总的来说,只是把数组第一位的值用第二位覆盖了,并没有删除元素,数组长度未变,最后一位变成未定义元素。需要新定义数组,将所有不是undefined值复制到新的数组中
    此方法不建议用于项目中。
Array.prototype.reIndex = function(myArr){
    const newArr = [];
    for(let i=0; i<myArr.length; i++) {
        if(myArr[i]!==undefined) {
            newArr.push(myArr[i])
        }
    }
    return newArr
}
Array.prototype.removeFirstPosition = function() {
    for(let i=0; i<this.length; i++){
        this[i] = this[i+1]
    }
    return this.reIndex(this)
}

let arr = [0, 1, 2, 3, 4, 5] ;
arr.removeFirstPosition(); //[1, 2, 3, 4, 5]

方法二:shift方法

let arr = [0, 1, 2, 3, 4, 5] ;
arr.shift(); // 0 (删除了0这个值)
//arr = [1, 2, 3, 4, 5]
  • 在任意位置添加或删除元素
    splice 方法
    splice方法接收的第一个参数,表示删除或插入的元素的索引值。第二个参数是删除元素的个数(不删除元素插入元素时传0)。第三个参数往后,就是要添加到数组里的值。
let arr= [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8]
arr.splice(5,3) // [2,3,4] 被删除的三个元素
// arr = [-3, -2, -1, 0, 1, 5, 6, 7, 8]

arr.splice(5,0,2,3,4) // [] 未删除元素
//arr = [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8]

arr.splice(5,3,10,11,12) // [2,3,4] 被删除的三个元素
// arr = [-3, -2, -1, 0, 1, 10, 11, 12, 5, 6, 7, 8]

注意:对于JavaScript的数组和对象, 还可以用delete运算符删除数组中的元素,例如delete arr[0]。然而,数组的位置0会变成undefined,也就是说arr[0] = undefined。因此,我们应该始终使用splicepopshift方法来删除数组的元素。

二维和多维数组

二维数组

定义一个二维数组,比如记录每天的每小时的气温。因为JavaScript不支持矩阵(二维数组,或数组中的数组),只支持一维数组,可以用数组套数组,实现矩阵活任一多维数组。

let averageTemp = [];
averageTemp[0] = [72, 75, 79, 79, 81, 81];
averageTemp[1] = [81, 79, 75, 75, 73, 73];

// 矩阵输出,做个通用函数,专门输出其中的值
function printMatrix(myMatrix) {
    for(let i=0; i< myMatrix.length; i++) {
        for(let j=0; j<myMatrix[i].length; j++) {
            console.log(myMatrix[i][j])
        }
    }
}
printMatrix(averageTemp);

多维数组

我们可以用这种方式来处理多维数组,假设我们要创建一个3x3x3的矩阵,每一格里包含矩阵的i(行)、j(列)、及z(深度)之和。

const matrix3x3x3 = [];
for(let i=0; i<3; i++ ) {
    matrix3x3x3[i] = [] //需要初始化每个数组
    for(let j=0; j<3; j++) {
        matrix3x3x3[i][j] = []
        for(let z=0; z<3; z++) {
            matrix3x3x3[i][j][z] = i+j+z;
        }
    }
}

// 输出矩阵
for(let i=0; i<matrix3x3x3.length; i++) {
    for(let j=0; j<matrix3x3x3[i].length; j++) {
        for(let z=0; z<matrix3x3x3[i][j].length; z++) {
            console.log(matrix3x3x3[i][j][z])
        }
    }
}

如果是一个3x3x3x3的矩阵,代码中就会用四层嵌套的for语句,以此类推。