思路:用双指针移动,来贪心的选择某些满足约束的数据构成结果集
11. 盛最多水的容器
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let max = 0;
let l = 0, r = height.length - 1;
while (l < r) {
max = Math.max(max, (r-l) * Math.min(height[l], height[r]));
if (height[l] < height[r]) {
l += 1;
} else {
r -= 1;
}
}
return max;
};
55. 跳跃游戏
/**
* @param {number[]} nums
* @return {boolean}
*/
var canJump = function(nums) {
let max = 0;
for (let i = 0; i < nums.length; i++) {
if (i > max) return false;
// 最远能够跳到的坐标位置。
max = Math.max(max, i + nums[i]);
if (max >= nums.length-1) return true;
}
return true;
};
45. 跳跃游戏 II
解法一:贪心
/**
* @param {number[]} nums
* @return {number}
*/
var jump = function(nums) {
// end 表示当前这次能够到达的最远位置,
// max 表示下一个最远的位置
let max = 0, end = 0, ans = 0;
for (let i = 0; i < nums.length - 1; i++) {
max = Math.max(max, i + nums[i]);
if (max >= nums.length-1) {
ans++;
return ans;
};
if (i === end) {
end = max;
ans++;
}
}
return ans;
};
解法二:动态规划
/**
* @param {number[]} nums
* @return {number}
*/
var jump = function(nums) {
let len = nums.length;
const dp = new Array(len).fill(Infinity);
dp[0] = 0;
for (let i = 0; i < nums.length - 1; i++) {
let right = Math.min(nums[i], nums.length - i - 1);
for (let j = 1; j <= right; j++) {
dp[i + j] = Math.min(dp[i + j], dp[i] + 1);
}
}
return dp[len-1];
};
455. 分发饼干
思路:总是找刚好满足孩子胃口的饼干,找到一个就匹配计数一次。
/**
* @param {number[]} g
* @param {number[]} s
* @return {number}
*/
var findContentChildren = function(g, s) {
g.sort((a, b) => a - b);
s.sort((a, b) => a - b);
let cl = g.length;
let sl = s.length;
let j = 0;
let ans = 0;
for (let i = 0; i < cl && j < sl; i++, j++) {
while (j < sl && s[j] < g[i]) {
j++;
}
if (j < sl) {
ans += 1;
}
}
return ans;
};
392. 判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,”ace”是”abcde”的一个子序列,而”aec”不是)。
进阶:
如果有大量输入的 S,称作 S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
致谢:
特别感谢 @pbrother添加此问题并且创建所有测试用例。
示例 1:
输入:s = “abc”, t = “ahbgdc” 输出:true
示例 2:
输入:s = “axc”, t = “ahbgdc” 输出:false
提示:
- 0 <= s.length <= 100
- 0 <= t.length <= 10^4
- 两个字符串都只由小写字符组成。
/** * @param {string} s * @param {string} t * @return {boolean} */ var isSubsequence = function(s, t) { const sl = s.length, tl = t.length; let si = 0, ti = 0; while (si < sl && ti < tl) { if (s[si] !== t[ti]) { ti += 1; } else { si += 1; ti += 1; } } return si === sl; };
624. 数组列表中的最大距离
难度中等53收藏分享切换为英文接收动态反馈
给定 m 个数组,每个数组都已经按照升序排好序了。现在你需要从两个不同的数组中选择两个整数(每个数组选一个)并且计算它们的距离。两个整数 a 和 b 之间的距离定义为它们差的绝对值 |a-b| 。你的任务就是去找到最大距离
示例 1:
输入: [[1,2,3], [4,5], [1,2,3]] 输出: 4 解释: 一种得到答案 4 的方法是从第一个数组或者第三个数组中选择 1,同时从第二个数组中选择 5 。
注意:
- 每个给定数组至少会有 1 个数字。列表中至少有两个非空数组。
- 所有 m 个数组中的数字总数目在范围 [2, 10000] 内。
- m 个数组中所有整数的范围在 [-10000, 10000] 内。
/**
* @param {number[][]} arrays
* @return {number}
*/
var maxDistance = function(arrays) {
let ans = Number.MIN_SAFE_INTEGER;
let min = arrays[0][0];
let max = arrays[0][arrays[0].length - 1];
for (let i = 1; i < arrays.length; i++) {
ans = Math.max(ans, Math.abs(min - arrays[i][arrays[i].length - 1]), Math.abs(arrays[i][0] - max));
min = Math.min(arrays[i][0], min);
max = Math.max(arrays[i][arrays[i].length - 1], max);
}
return ans;
};
44. 通配符匹配
思路一:动态规划
要点:
i,j 分别指向s和p的当前遍历下标
1、* 字符匹配任意个字符,所以当前结果与之前结果保持一致,即
dp[i][j] = dp[i][j-1] || dp[i-1][j]
2、?或者字符完全匹配,则
dp[i][j] = dp[i-1][j-1]
3、base case:
- 空串匹配:dp[0][0] = true
‘‘字符匹配:dp[0][i] = true, p[i-1] === ‘‘
/** * @param {string} s * @param {string} p * @return {boolean} */ var isMatch = function(s, p) { const dp = makeArray(s.length + 1, p.length + 1, false); dp[0][0] = true; // 空串空模式匹配。 for (let i = 1; i <= p.length; i++) { if (p[i-1] === '*') { dp[0][i] = true; // 空串非空模式匹配边界,若出现非*的模式,则为false。 } else { break; } } for (let i = 1; i <= s.length; i++) { for (let j = 1; j <= p.length; j++) { const pp = p.charAt(j-1); if (pp === '*') { dp[i][j] = dp[i][j - 1] || dp[i - 1][j]; } else if (pp === '?' || pp === s.charAt(i-1)) { dp[i][j] = dp[i-1][j-1]; } } } return dp[s.length][p.length]; }; function makeArray(m, n, value) { const r = new Array(m); for (let i = 0; i < m; i++) { r[i] = new Array(n).fill(value); } return r; }
思路二:使用双指针贪心 + 回溯
/** * @param {string} s * @param {string} p * @return {boolean} */ var isMatch = function(s, p) { let left = 0, right = 0; let preLeft = -1, preRight = -1; while (left < s.length) { // case 1: ? 以及字符匹配场景 if (right < p.length && (p.charAt(right) === '?' || p.charAt(right) === s.charAt(left))) { left ++; right++; continue; } // case 2: * 场景,贪心选择匹配一个字符,并记录当前left,和right的位置,方便后面发现问题时回溯。 if (preRight < p.length && p.charAt(right) === '*') { preLeft = left; // 记录星号 // 并且right移到下一位,准备下个循环s[left]和p[right]的匹配 //(也就是匹配0个字符) preRight = right++; continue; } // case 3: 匹配不成功,说明前面的*匹配有错,需要回溯。 if (preLeft >= 0) { // left 回溯到 preLeft + 1,显然匹配字符的量出错,现在多匹配一个,且自身加一,方便后面如果发现有问题,再次回溯。 left = ++ preLeft; // right 回溯到preRight + 1 重新使用p串*后的部分开始对齐s串preLeft right = preRight + 1; continue; } // case 4: 所有匹配场景都不匹配,则匹配失败。 return false; } // case 5 : 若right后面的模式串没有被匹配,则需要检查剩下的是否都是*,否则匹配失败。 while (right < p.length && p[right] == '*') ++right; return right === p.length; };