数组
注意!!!!
用 js 写代码时,运算结果一定要注意是否取整!!!对于不能整除的算式结果, js 会保留小数,而不像C、C++等会自动取整,一定不要忘记。
1. 二分查找法
题目:
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
重点:
-
区间的定义(左闭右闭 还是 左闭右开)
-
根据区间的定义决定:
- right 的初始值
- while (left ??? right) ???要使用 <= 还是 <
- nums[middle] != target 时 left 和 right 的更改
-
遵循循环不变量原则,即区间类型不变,即可解决2中疑问
-
注意点:计算middle时,要对结果进行取整操作(使用js时)
代码:
-
左闭右闭
/** * @param {number[]} nums * @param {number} target * @return {number} */ var search = function (nums, target) { let left = 0 let right = nums.length - 1 while (left <= right) { let middle = Math.floor((left + right) / 2) //向下舍入 if (nums[middle] > target) right = middle - 1 else if (nums[middle] < target) left = middle + 1 else return middle } return -1 }; -
左闭右开
/** * @param {number[]} nums * @param {number} target * @return {number} */ var search = function (nums, target) { let left = 0 let right = nums.length while (left < right) { let middle = Math.floor((left + right) / 2) //向下舍入 if (nums[middle] > target) right = middle else if (nums[middle] < target) left = middle + 1 else return middle } return -1 };
2. 移除元素
题目:
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
双指针法:
即快慢指针法: 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
思路:
-
定义快慢指针
- 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
- 慢指针:指向更新新数组下标的位置
-
关键:要明确快慢指针的含义,这样后面的思路才能容易理解。
-
注意:双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组、链表、字符串等操作的面试题,都使用双指针法。
过程:
-
暴力解法
-
双指针法
代码:
-
暴力解法
/** * @param {number[]} nums * @param {number} val * @return {number} */ var removeElement = function (nums, val) { let len = nums.length for (let i = 0; i < len; i++) { if (nums[i] === val) { for (let j = i; j < nums.length - 1; j++) { nums[j] = nums[j + 1] } len-- i-- } } return len }; -
双指针法
/** * @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 };
3. 有序数组的平方
题目:
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
注意点:数字有正有负(主要考虑这种情况)
思路(数字有正有负):
-
数组是有序的,但负数平方之后可能会打乱顺序。
-
数组平方的值从数组的两端向中间递减。
-
故考虑双指针法,i指向起始位置,j指向终止位置,对比i,j位置对应值的大小。则:
定义一个新数组result,和nums数组一样的大小,让k指向result数组终止位置。
- 如果nums[i] * nums[i] > nums[j] * nums[j] 那么result[k--] = nums[i] * nums[i] 并移动 i 。
- 如果nums[i] * nums[i] <= nums[j] *nums[j] 那么result[k--] = nums[j] * nums[j] 并移动 j 。
直至 i > j 结束。
过程:
代码:
/**
* @param {number[]} nums
* @return {number[]}
*/
var sortedSquares = function (nums) {
let result = [];
let k = nums.length - 1;
for (let i = 0, j = k; i <= j;) {
if (nums[i] * nums[i] > nums[j] * nums[j]) {
result[k--] = nums[i] * nums[i]
i++
} else {
result[k--] = nums[j] * nums[j]
j--
}
}
return result
};
4. 长度最小的子数组
题目:
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度**。**如果不存在符合条件的子数组,返回 0 。
滑动窗口:
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。 在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环完成了一个不断搜索区间的过程。而这里我们使用一个for循环来完成操作。
思路:
-
用一个for循环,表示的是滑动窗口的起始位置,还是终止位置?——终止位置。若为起始位置,则又将陷入暴力解法。
-
确定如下三点:
- 窗口内是什么?——满足其和 ≥ target 的长度最小的连续子数组。
- 如何移动窗口的起始位置?——如果当前窗口的值大于target了,那么窗口就要向前移动了(即缩小窗口)。
- 如何移动窗口的终止位置?——for循环里的控制变量 j 的值即为终止位置。
-
关键:理解滑动窗口起始位置的移动策略。
-
注意:对于子数组、子串相关问题可以优先考虑使用滑动窗口。
过程:
代码:
/**
* @param {number} target
* @param {number[]} nums
* @return {number}
*/
var minSubArrayLen = function (target, nums) {
let sum = 0;
let len = nums.length + 1;
let i = 0;
for (let j = 0; j < nums.length; j++) {
sum += nums[j]
while (sum >= target) {
len = len > (j - i + 1) ? (j - i + 1) : len
sum -= nums[i]
i++
}
}
return len === (nums.length + 1) ? 0 : len
};
5. 螺旋矩阵 II
题目:
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
思路:
-
模拟矩阵生成过程,坚持循环不变量原则(区间——采用左闭右开)
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
-
确定循环次数—— n / 2
-
考虑特殊情况—— n 为奇数时,中间元素需要单独赋值
-
js 中多维数组的定义
参考:js 多维数组
-
注意点:n 为奇数时,js 中 n / 2 为小数,故需要进行取整操作
图示:
代码:
/**
* @param {number} n
* @return {number[][]}
*/
var generateMatrix = function (n) {
let matrix = new Array(n).fill(0).map(() => new Array(n).fill(0))
let start = 0
let end = n - 1
let k = 1;
let num = Math.floor(n / 2)
let mid = num
while (num--) {
for (let j = start; j < end; j++) {
matrix[start][j] = k++
}
for (let i = start; i < end; i++) {
matrix[i][end] = k++
}
for (let j = end; j > start; j--) {
matrix[end][j] = k++
}
for (let i = end; i > start; i--) {
matrix[i][start] = k++
}
start++;
end--;
}
if (n % 2 != 0) {
matrix[mid][mid] = k
}
return matrix
};