数组和链表的区别
| 数组 | 链表 |
|---|---|
| 数组是在内存中是连续分布。 | 链表在内存中不是连续分布的。 |
| 数组在定义的时候,长度就是固定的,如果想改动数组的长度,就需要重新定义一个新的数组。 | 链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景。 |
704. 二分查找
var search = function(nums, target) {
// TODO: 确定初始左右边界
let left=0, right = nums.length-1
// TODO: while循环中 二分不断缩小范围找到traget的下标
// 边界始终始终是左闭右闭[left, right]
// 所以,left == right 有意义,但是left > right 时没有意义,比如[2, 1],此时跳出while
while(left<=right){
// TODO: 计算mid
// 防止溢出,注意向下取整
let mid = Math.floor(left + (right-left)/2)
let val = nums[mid]
// TODO: 如果中间值小于目标值往中间值右边找,大于目标值往中间值左边找,相等返回结果。
if (val === target) return mid
else if (val < target) left = mid + 1
else right = mid - 1
}
return -1
};
27. 移除元素
题目注意:
- 原地删除(考虑双指针)
- 要删除的val可能存在多个
示例 1:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
var removeElement = function(nums, val) {
// TODO: 初始化双指针
let left = right = 0
// TODO: 用right指针遍历nums数组,超过nums长度就退出循环
while(right < nums.length) {
// TODO: 分情况讨论
// right不是要删除的val:用right覆盖left的值,left再++,始终保证[0, left)区间的值没有val,[left, ...)有val;right++继续遍历下一个值
// right是要删除的val:跳过val/right值,right++继续遍历下一个值
if ( nums[right] !== val) {
nums[left] = nums[right]
left++
}
right++ // 无论如何right++继续遍历下一个值
}
return left // 删除val后的数组[0,left)长度就是left
};
977.有序数组的平方
题目注意:
- 非递减顺序:数组升序,还可能元素相等
- 返回新数组(考虑新建数组)
示例 1:
输入: nums = [-4,-1,0,3,10]
输出: [0,1,9,16,100]
解释: 平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
示例 2:
输入: nums = [-7,-3,2,3,11]
输出: [4,9,9,49,121]
var sortedSquares = function(nums) {
// TODO: 初始化双指针
let left = 0, right = idx = nums.length - 1
// TODO: 创建一个新数组,初始化为0
let res = new Array(nums.length).fill(0)
// TODO: 遍历一遍nums数组
// left从头开始,right从尾开始,left>right说明每一个元素都遍历了一遍退出循环
while(left <= right) {
// TODO: 准备好left平方值,和right平方值
let left_squares = nums[left]*nums[left]
let right_squares = nums[right]*nums[right]
// TODO: 从nums两头取元素,计算平方值,从res的右往左填写
// nums两头元素的平方值一定是最大的,而返回结果res必须是非递减顺序,所以从大往小,即从右往左填写res最方便
// 如果left平方大,res填写left平方;如果right平方大,res填写right平方
// idx控制res数组位置。填完idx--,left++或者right--
// 注意:=在哪里无所谓
if (left_squares >= right_squares) {
res[idx--] = left_squares
left++
}else {
res[idx--] = right_squares
right--
}
}
return res
};
209.长度最小的子数组
题目注意:
- 返回:长度最小的>=target的连续集合
- 值的总和>=target
- 最小(收缩滑动窗口)
- 连续子数组(连续 考虑滑动窗口)
示例 1:
输入: target = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入: target = 4, nums = [1,4,4]
输出: 1
示例 3:
输入: target = 11, nums = [1,1,1,1,1,1,1,1]
输出: 0
- 滑动窗口,是双指针的一种
- 重点1:第一个while循环是移动起始位置,还是终止位置?
- while里的必须移动终止位置:因为如果是移动起始位置,那么还得用2个for循环遍历每个起始位置和终止位置的数组
- 重点2:如何移动起始位置?
- right指针遍历每一个终止位置,用滑动的策略移动起始位置
- 重点1:第一个while循环是移动起始位置,还是终止位置?
var minSubArrayLen = function(target, nums) {
let left = right = 0
let minL = Infinity
let sum = 0
// TODO: right指针遍历nums数组每个元素,超过nums长度就退出循环
while (right < nums.length){
// TODO: 扩大滑动窗口
sum+=nums[right]
// TODO: 如果集合值总和sum 大于等于 target,集合满足条件,就记录最小长度minL,再缩小窗口,直到获取最小长度的窗口。
// 必须用while而不是if,是为了缩小滑动窗口,找到最小的集合。比如 1 1 1 100 , target = 100, 用if,结果集合是 [1,1,1,100],用while, 结果集合是最小的 [1,100]
while(sum >= target){
// TODO: 记录满足条件的最小集合长度
subL = right - left + 1 // 集合的长度
minL = Math.min(minL, subL) // 最小集合长度
// TODO: 缩小滑动窗口,找到最小的符合 >=target的集合
sum-=nums[left]
left++
}
right++ // 遍历每一个终止位置
}
return minL === Infinity ? 0 : minL
};
59.螺旋矩阵II
模拟过程,不涉及算法,十分考察对代码的掌控能力
示例 1:
输入: n = 3
输出: [[1,2,3],[8,9,4],[7,6,5]]
示例 2:
输入: n = 1
输出: [[1]]
var generateMatrix = function(n) {
// TODO: 初始化
// loop - 循环圈数, mid - 最中间的的位置
let loop = mid = Math.floor(n/2)
// 每圈初始坐标
let startX = startY = 0
let endX = endY = n-1
// i,j也可以在每个for循环里定义
let i, j
// 结果是一个二维数组
let res = new Array(n).fill(0).map(()=> new Array(n).fill(0))
// 数组里的值是递增的
let val = 1
// TODO: 每次while循环填写一圈的值。
// loop减到0,退出循环。
while(loop--){
// 填写上边:i固定startX,j移动填写[startY, endY)
for(j = startY; j < endY; j++) res[startX][j] = val++
// 右边:j固定endY,i移动[startX, endX)
for(i = startX; i < endX; i++) res[i][endY] = val++
// 下边:i固定endX,j移动[endY, startY)
for(j = endY; j > startY; j--) res[endX][j] = val++
// 左边:j固定startY,i移动[endX, startX)
for(i = endX; i > startX; i--) res[i][startY] = val++
// 起止位置、终止位置缩小1(边界缩小一圈)
startX++
startY++
endX--
endY--
}
// n是奇数,填写漏下的最中间的值
if (n%2===1) res[mid][mid] = val
return res