我们经常会用两个指针来处理一些问题,这里对套路做简单的总结。
模版:
注意,双指针使用的条件:因为核心是缩小求解范围(l 和 r 之间的范围),所以数组必须满足一定的规律,才行:比如 15 和 881 都是有序数组才行。
function test(nums) {let n = nums;let l = 0;let r = n - 1;while (l < r) { // l r 指向未比较的地方if ('condition1') {// 两边同时缩r--;l++;} else if ('condition2') {r--; // 只右边缩} else if ('condition3') {l++; // 只左边缩}}}
15. 三数之和
https://leetcode-cn.com/problems/3sum/solution/hua-jie-suan-fa-15-san-shu-zhi-he-by-guanpengchn/
直接暴力方法是 三层 for 循环:
1)第一层 for 循环,先固定一个数,
2)之后就是两数之和的问题,这里有 两层 for 循环
另外的还有巧妙的方法:如果对于原数组不加任何处理,那么很难有提高,我们需要利用有序数组,在计算累加和的优势。
对于 一个 升序数组,如果数组元素 已经大于 目标和 了,就没有必要向后递归判断了。
// 模式识别:题目中有不重复 -> 数组排序 和 按照一定模式去做// 如何缩小查找范围var threeSum = function(nums) {let n = nums.length;if (n <= 2) {return [];}let result = [];// nums = nums.sort(); // 默认升序 -- 这个排序有问题nums = nums.sort((a, b) => a - b);let l;let r;for (let i = 0; i < n; i++) {let num = nums[i];if (num > 0) {break;}if (i > 0 && nums[i] === nums[i - 1]) { // 去重:理解困难一些continue;}l = i + 1;r = n - 1;// 据说是经典双指针操作:while (l < r) { // l r 指向未比较的地方let sum = num + nums[l] + nums[r];if (sum === 0) {result.push([num, nums[l], nums[r]]);while(nums[r] === nums[r - 1]) { // 减到最后一个相同r--;}while(nums[l] === nums[l + 1]) { // 加到最后一个相同l++;}r--;l++;} else if (sum > 0) {r--;} else {l++;}}}return result;}
349. 两个数组的交集
特别有创意!
var intersection = function(nums1, nums2) {let n1 = nums1.length;let n2 = nums2.length;nums1.sort((a, b) => a - b);nums2.sort((a, b) => a - b);let i1 = 0;let i2 = 0;let ans = [];while (i1 < n1 && i2 < n2) { // 很经典的双指针let num1 = nums1[i1];let num2 = nums2[i2];if (num1 === num2) {if ((ans.length === 0) || ans[ans.length - 1] !== num1) {ans.push(num1);}i1++;i2++;} else if (num1 < num2) {i1++;} else {i2++}}return ans;};
881. 救生艇
和 15 很像,但是很简单,操作的也只有两个元素,所以少一层for循环。
var numRescueBoats = function(people, limit) {let n = people.length;people = people.sort((a, b) => a - b);if (people[n - 1] > limit) {return null; // 船的容量小于最重的人,这活干不了}let light = 0;let heavy = n - 1;let result = [];while (light < heavy) {if (people[light] + people[heavy] <= limit) {result.push([people[light], people[heavy]]);light++;heavy--;} else {result.push([people[heavy]]);heavy--;}}return result.length;};
但是有问题,有这样一种case特别烦人:
numRescueBoats([3,2,2,1], 3)
排序之后 是 [1, 2, 2, 3]
[3], [1,2]被取走之后,只剩下[2],而此时 l 和 r 指向这同一个位置。
要把这个逻辑补上,得写一堆代码,但是有简单方法:(个人不喜欢)
// 改为while (light <= heavy) {
11. 盛最多水的容器
这个乍一看和 “接雨水”问题很像,但是却可以使用 双指针去做,但是我还是不很了解,但是求解过程是熟悉。
看这个题解:https://leetcode.cn/problems/container-with-most-water/solution/container-with-most-water-shuang-zhi-zhen-fa-yi-do/
var maxArea = function(height) {let n = height.length;let left = 0;let right = n - 1;let max = 0;while (left < right) {let area = (right - left) * Math.min(height[left], height[right]);if (max < area) {max = area;}if (height[left] < height[right]) {left++;} else {right--;}}return max;};
