数据结构
数组的创建
const arr = [1, 2, 3]; // 赋初始值
const arr = new Array(); // 等价于 `const arr = [];`
const arr = new Array(6); // 声明长度
const arr = new Array(6).fill(1); // 声明长度,赋初值
数组的遍历
- for 循环,性能最快
- forEach 方法,访问每一个元素的值和下标
- 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;
};