「算法」JS 中的数组

315 阅读3分钟

数据结构

数组的创建

const arr = [1, 2, 3]; // 赋初始值
const arr = new Array(); // 等价于 `const arr = [];`
const arr = new Array(6); // 声明长度
const arr = new Array(6).fill(1); // 声明长度,赋初值

数组的遍历

  1. for 循环,性能最快
  2. forEach 方法,访问每一个元素的值和下标
  3. map 方法,接收一个函数对数组内容处理,返回一个新的数组

二维数组

不要使用 const arr = (new Array(6)).fill([]) 对二维数组初始化,因为 fill 方法接收的是一个参数的引用,这样做只是对最外层数值的每一个元素赋了同一个数组的引用。可以这样赋值:

const arr = new Array(6).fill(1).map(() => new Array(6).fill(1));

( map 的 callback 函数只会在有值的索引上被调用)

或者使用双重循环赋值。

数组中还有一些常用的方法:

arr.slice(begin, end); // 对原数组 [begin, end) 的元素做浅拷贝返回,原始数组值不变
arr.splice(start[, deleteCount[, item1[, item2[, ...]]]]) // 直接修改原始数组
arr.unshift(item); // 数组首部添加元素

更多方法查阅这里

算法

数组的应用

关键词:将求和问题转化为求差问题、Map 对象、双指针

1. 两数之和

这道题使用两层循环来解答的话,时间复杂度为 O(n^2)。

我们可以用空间换时间,将求和问题转化为求差问题。使用 Map 对象来存放已经遍历过的元素,key 存放元素的值, value 存放元素的索引。每当遍历到一个新元素时在 Map 中查询是否存在 target 与改元素的差值,若存在,则得出答案。

var twoSum = function(nums, target) {
    const storage = new Map();
    for (let i = 0; i < nums.length; i++){
        if (storage[target - nums[i]] === undefined){
            storage[nums[i]] = i;
        } else {
            return [storage[target - nums[i]], i];
        }
    }
};

88. 合并两个有序数组

解题思路是使用双指针分别指向两个数组有效部分的尾部,比较指针指的两个元素,将较大的填入 nums1 的尾部,然后再左移相应的指针,直到结束。

var merge = function(nums1, m, nums2, n) {
    let i = m - 1, j = n - 1, k = m + n - 1;
    while (i >= 0 && j >= 0) {
        if (nums1[i] > nums2[j]){
            nums1[k--] = nums1[i--];
        } else {
            nums1[k--] = nums2[j--];
        }
    }
    while (j >= 0) {
        nums1[k--] = nums2[j--];
    }
};

15. 三数之和

这道题,我们还是将求和问题转化为求差问题,固定一个数 a,使用双指针来确定剩余的两个数。

**双指针可以用空间换时间,也可以降低问题复杂度,它需要数组有序。**所以我们需要先将数组排序。

双指针的首和尾分别指向固定数字的下一个元素 b 和数组的最后一个元素 c。如果 a + b + c > 0,则右指针左移,若 < 0,则左指针右移。同时还要处理重复元素的情况。

var threeSum = function(nums) {
    const answer = [];
    nums = nums.sort((a, b) => {
        return a - b;
    })
    for(let i = 0; i < nums.length - 2; i++) {
        let j = i + 1, k = nums.length - 1;
        if (i > 0 && nums[i] === nums[i-1]){
            continue;
        }
        while (j < k) {
            if (nums[i] + nums [j] + nums[k] > 0) {
                k--;
            } else if (nums[i] + nums [j] + nums[k] < 0) {
                j++;
            } else {
                answer.push([nums[i], nums[j], nums[k]]);
                k--;
                j++;
                while(nums[k + 1] === nums[k]){
                    k--;
                }
                while(nums[j - 1] === nums[j]){
                    j++;
                }
            }
        }
    }
    return answer;
};

字符串的应用

关键词:对称性、双指针、Map 对象、正则表达式、捕获组

9. 回文数

合理使用 API:

var isPalindrome = function(x) {
    return x.toString() === x.toString().split("").reverse().join("") ? true : false;
};

或者利用对称性使用 for 循环判断。

剑指 Offer II 019. 最多删除一个字符得到回文

判断回文 -> 对称性 + 双指针。

双指针指向字符串的首和尾,逐个检查指向的元素是否相等,若不相等,判断 (左指针, 右指针] 或者 [左指针, 右指针) 是否是回文字符串,若有一个是则通过。

var validPalindrome = function(s) {
    let i = 0, j = s.length-1;
    while(1) {
        if (i >=j ) {return true}
        else if (s[i] === s[j]) {
            i++;
            j--;
            continue;
        } else {
            if(isPalindrom(i,j-1) || isPalindrom(i+1,j)) {
                return true;
            } else {
                return false;
            }
        }
    }
    function isPalindrom (start, end) {
        return s.slice(start, end + 1) === 
            s.slice(start, end +1 ).split("").reverse().join("") ? true : false;
    }
};

211. 添加与搜索单词

如果我们定义一个数组存放字符串,简单粗暴地使用正则表达式去查找的话,会超出时间限制:

var WordDictionary = function() {
    this.words=[];
};

/** 
 * @param {string} word
 * @return {void}
 */
WordDictionary.prototype.addWord = function(word) {
    this.words.push(word);
};

/** 
 * @param {string} word
 * @return {boolean}
 */
WordDictionary.prototype.search = function(word) {
    const reg = new RegExp("^"+word+"$");
    return this.words.some((item)=>{
        return reg.test(item);
    })
};

我们可以定义一个 Map 对象,将不同长度的字符串分数组来存放,这样就降低了搜索的时间:

var WordDictionary = function() {
    this.words= {};
};

/** 
 * @param {string} word
 * @return {void}
 */
WordDictionary.prototype.addWord = function(word) {
    const len = word.length;
    if(this.words[len]===undefined){
        this.words[len] =  [];
    }
    this.words[len].push(word);
};

/** 
 * @param {string} word
 * @return {boolean}
 */
WordDictionary.prototype.search = function(word) {
    const len =word.length;
    if(this.words[len]===undefined) {
        return false;
    } else {
        const reg = new RegExp("^"+word+"$");
        return this.words[len].some((item)=>{
            return reg.test(item);
        })
    }
};

剑指 Offer 67. 把字符串转换成整数

这里还是使用正则表达式解决问题,通过正则表达式来捕获满足要求的字符串,根据题目的要求一步步判断即可。

其中,正则表达式是\s*([-\+]?[0-9]*).*捕获组用小括号包围,是 [-\+]?[0-9]*,通过 match 方法获取匹配的结果,结果是一个数组,第 0 项是完整的匹配,之后的项是匹配的捕获组。

/**
 * @param {string} str
 * @return {number}
 */
var strToInt = function(str) {
    const reg = /\s*([-\+]?[0-9]*).*/;
    const groups= str.match(reg);
    let res = 0;
    const max = Math.pow(2,31)-1;
    const min = - max -1;
    if(groups){
        res = +groups[1];
        console.log(groups[1])
        if(isNaN(res)) {
            return 0;
        } else if(res>max) {
            return max;
        } else if (res<min) {
            return min;
        } else {
            return res;
        }
    }
    return 0;
};