引言
由于在某站看见一位前辈用双指针也写了一下这道题 于是乎又拿出来看看
题目
两数之和
给定一个整数数组 nums 和一个整数目标值 target,
请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标。
相信各位都不陌生,力扣的入门题,暴力、哈希表相信各位都随手拈来了 今天看见前辈的双指针 边学习了一番,在此记录分享
var twoSum = function(nums, target) {
const indexArray = [];
for (let i = 0; i < nums.length; i++) {
indexArray.push(i);
}
// 根据原数组 nums 的值对 indexArray 进行排序
indexArray.sort((a, b) => nums[a] - nums[b]);
// 使用双指针解决问题
let left = 0;
let right = nums.length - 1;
while (left < right) {
const sum = nums[indexArray[left]] + nums[indexArray[right]];
if (sum === target) {
return [indexArray[left], indexArray[right]];
} else if (sum < target) {
left++;
} else {
right--;
}
}
// 如果没有找到满足条件的两个数,返回空数组
return [];
};
首先创建一个新的数组 indexArray,用于存储原始数组 nums 的下标。然后,根据原数组 nums 的值对 indexArray 进行排序。接下来,使用双指针 left 和 right 来遍历排序后的 indexArray,在每一步迭代中计算指针指向的两个数之和,并与目标值 target 进行比较。最后返回满足条件的两个数的下标。
看来双指针是个好用法子,于是我又找了两道题试试。嘿嘿~
三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]]
满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。
请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
思考
- 非常显然,这题目正符合我们的胃口,双指针的思路很快就出现在了脑子里,不过双指针的前提是有序队列,这样才更有意义,因此我们首先要对数组进行排序
- 题目说返回不重复的三元组,所以可能有相同的元素,我们要找三个数,因此在外循环中,我们需要选定一个数,暂时不动,再用双指针寻找。
- 这里有个小细节,如果前多个元素是一样的,毫无疑问我们要跳过的,那么是要第一个还是最后一个呢?虽然不是很难,但鄙人不才,疏忽了。(应该是要第一个,否则可能会遗漏)
- 那么选定了一个,后面的
nums.length-1个就交给双指针去寻找吧
所以 我们也就有了结果
var threeSum = function(nums) {
var result = []; // 存储结果的数组
// 先对数组进行排序
nums.sort((a, b) => a - b); // 使用快速排序算法对数组进行升序排序
// 遍历数组,查找三个数之和等于0的组合
for (var i = 0; i < nums.length - 2; i++) {
// 跳过重复的元素,确保不重复处理相同的数
if (i > 0 && nums[i] === nums[i - 1]) continue;
var target = -nums[i]; // 将问题转化为在剩余数组中找两数之和等于target
var left = i + 1; // 左指针指向当前元素的下一个位置
var right = nums.length - 1; // 右指针指向数组末尾
// 使用双指针法寻找两数之和等于target的组合
while (left < right) {
var sum = nums[left] + nums[right]; // 计算两数之和
if (sum === target) {
// 找到满足条件的组合,加入到结果数组中
result.push([nums[i], nums[left], nums[right]]);
// 跳过重复的元素,确保不重复处理相同的数
while (left < right && nums[left] == nums[left + 1]) left++;
while (left < right && nums[right] == nums[right - 1]) right--;
left++; // 左指针右移
right--; // 右指针左移
} else if (sum < target) {
left++; // 和小于target,左指针右移
} else {
right--; // 和大于target,右指针左移
}
}
}
return result; // 返回结果数组
};
盛水最多的容器
题目
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。
这道题也是思路清晰多了 我们直接开始
在这个问题中,我们可以使用两个指针 left 和 right,分别指向数组的起始和结束位置。 然后,我们计算由当前指针所指的线段构成的容器的容量,并记录最大容量。 接着,我们将指向较短线段的指针向中间移动一步(因为移动较长线段的指针不会使容器的容量增加,而只会减少容器的宽度), 直到两个指针相遇为止。
var maxArea = function(height) {
// 初始化最大面积为 0
let maxArea = 0;
// 左指针初始位置
let left = 0;
// 右指针初始位置
let right = height.length - 1;
// 当左指针小于右指针时,进行循环
while (left < right) {
// 计算当前左右指针所指的线段的高度的最小值
const minHeight = Math.min(height[left], height[right]);
// 计算当前容器的宽度
const width = right - left;
// 计算当前容器的面积
const area = minHeight * width;
// 更新最大面积
maxArea = Math.max(maxArea, area);
// 移动较短的线段指针,以寻找更高的线段
if (height[left] < height[right]) {
left++; // 如果左边的线段较短,则将左指针向右移动
} else {
right--; // 如果右边的线段较短或相等,则将右指针向左移动
}
}
// 返回最大面积
return maxArea;
};
总结
双指针法常用于以下情况:
- 有序数组/链表:在有序数组或链表中查找特定元素或满足特定条件的组合。
- 求和问题:如两数之和、三数之和等。
- 反转问题:如反转字符串、反转链表等。
是一种简单且高效的解决问题的方法,适用于很多类型的问题,包括数组和链表。掌握了双指针法的基本思想和应用场景,能够帮助我们更快地解决问题,并写出高效的算法。