双指针
移动零
思路:
利用快慢指针法
- 快指针用来寻找数组中的非零元素
- 慢指针用来指向非零元素需要更新到的位置的下标
代码:
/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/
var moveZeroes = function (nums) {
let fast = 0, slow = 0
while (fast < nums.length) {
if (nums[fast] !== 0) {
[nums[fast], nums[slow]] = [nums[slow], nums[fast]]
slow++
}
fast++
}
};
盛最多水的容器
思路:
- 初始化: 双指针 l, r 分别指向水槽的左右两端;
- 循环收窄: 直至双指针相遇时跳出;
- 更新面积最大值 max ;
- 选定两板高度中的短板,向中间收窄一格 (或收窄至板高不小于当前短板);
- 返回值: 返回面积最大值 max 即可;
代码:
-
完整版
/** * @param {number[]} height * @return {number} */ var maxArea = function (height) { let max = 0, l = 0, r = height.length - 1 while (l < r) { let hl = height[l] let hr = height[r] let h = hl > hr ? hr : hl let m = h * (r-l) max = max > m ? max : m if (hl > hr) { while (height[--r] < hr) { } } else { while (height[++l] < hl) { } } } return max }; -
缩减版
/** * @param {number[]} height * @return {number} */ var maxArea = function (height) { let max = 0, l = 0, r = height.length - 1 while (l < r) { max = height[l] < height[r] ? Math.max(max, (r - l) * height[l++]) : Math.max(max, (r - l) * height[r--]) } return max };
三数之和
思路:
- 将数组按升序排序
- 从左侧开始,选定一个值为定值 ,右侧进行求解,获取与其相加为 0 的两个值
- 类似于快排,定义首和尾
- 首尾与定值相加
- 等于 0,记录这三个值
- 小于 0,首部右移
- 大于 0,尾部左移
- 定值右移,重复该步骤
- 剪枝及去重:
- 剪枝:定值的位置应与数组末尾至少相隔一个数;定值若大于 0 ,则直接返回结果
- 去重:定值、首尾值都不应重复,若重复则直接跳过
注意:
Array.prototype.sort() 方法就地对数组的元素进行排序。
-
默认排序是将元素转换为字符串,然后按照它们的 UTF-16 码元值升序排序。在数值排序中,9 出现在 80 之前,但因为数字会被转换为字符串,在 Unicode 顺序中 “80” 排在 “9” 之前。所有的
undefined元素都会被排序到数组的末尾。 -
如果提供了
compareFn,所有非undefined的数组元素都会按照比较函数的返回值进行排序(所有的undefined元素都会被排序到数组的末尾,并且不调用compareFn)compareFn(a, b)返回值排序顺序 > 0 a在b后,如[b, a]< 0 a在b前,如[a, b]=== 0 保持 a和b原来的顺序
代码:
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function (nums) {
let res = []
nums.sort((a, b) => a - b)
for (let s = 0; s < nums.length - 2; s++) {
if (nums[s] > 0) return res
if (s > 0 && nums[s] === nums[s - 1]) continue
let l = s + 1, r = nums.length - 1
while (l < r) {
let sum = nums[s] + nums[l] + nums[r]
if (sum > 0)
r--
else if (sum < 0)
l++
else {
res.push([nums[s], nums[l], nums[r]])
while (nums[l] === nums[++l] && l < r) { }
while (l < r && nums[r] === nums[--r]) { }
}
}
}
return res
};
接雨水
思路:
-
按列计算
分别计算除左右两端之外所有列的接水量并求和:
-
每列接水量为其左侧最高柱与右侧最高柱中的较低柱与该列高度差。(注意:当差小于 0 时,此列接水量也应为 0,即只有差大于 0 时,才需要进行求和操作)
-
每列的左侧最高柱高度为前一列的左侧最高柱与前一列相比较高者的高度,
每列的右侧最高柱高度为后一列的右侧最高柱与后一列相比较高者的高度。
故,
- 从前往后遍历数组,求取每列柱子的左侧最高柱并记入 lMax 数组
- 从后往前遍历数组,求取每列柱子的右侧最高柱并记入 rMax 数组
- 遍历柱子数组,进行接水量计算及求和操作
- 返回结果
-
-
单调栈(按行计算)
-
使用单调递减栈(原因:当当前柱子高于前一个柱子时,则可开始计算接水量)
栈顶元素即接水柱子 cur ,栈顶第二个元素即左边柱子 l,当前柱子即右边柱子 r
-
初始化:将第一个柱子入栈
-
遍历剩余柱子:
-
若栈不空,且当前柱子 i (r)高于栈顶柱子,则:(可开始计算接水量)
-
将栈顶柱子记录并出栈
-
判断此时栈是否为空,
若空,则不做处理(即无左柱子无法接水);
若不空,则计算接水量 sum:
width = r - l -1
heigth = Math.min(height[l], height[r]) - height[cur]
sum = width * height -
若仍满足条件,则一直重复以上两步
-
-
将当前柱子 i 进栈
-
-
返回结果
-
代码:
-
双指针
/** * @param {number[]} height * @return {number} */ var trap = function (height) { if (height.length < 3) return 0 let res = 0, lMax = [], rMax = [] lMax[0] = 0, rMax[height.length - 1] = 0 // 记录每个柱子的左边柱子最大高度 for (let i = 1; i < height.length - 1; i++) lMax[i] = Math.max(lMax[i - 1], height[i - 1]) // 记录每个柱子的右边柱子最大高度 for (let i = height.length - 2; i > 0; i--) rMax[i] = Math.max(rMax[i + 1], height[i + 1]) // 求和 for (let i = 1; i < height.length - 1; i++) { let h = Math.min(lMax[i], rMax[i]) - height[i] if (h > 0) res += h } return res }; -
单调栈
/** * @param {number[]} height * @return {number} */ var trap = function (height) { let res = 0, s = [0] for (let i = 1; i < height.length; i++) { while (s.length > 0 && height[i] > height[s[s.length - 1]]) { let cur = s[s.length - 1] s.pop() if (s.length < 1) break res += ((Math.min(height[s[s.length - 1]], height[i]) - height[cur]) * (i - s[s.length - 1] - 1)) } s.push(i) } return res };