Leetcode双指针-day2
记录自己刷力扣备战秋招的刷题笔记❤️
------wosz
双指针
双指针可以说就是一种技巧,在遍历的时候,为了减少遍历的次数我们可以使用双指针进行减少遍历的一个次数进一步减少复杂度。

就如图中的形式这两个指针可以是对同一数组 进行操作同时也可以是对两个数组进行操作。
双指针大致可以分为三类:
- 普通双指针:两个指针朝同一方向进行移动
- 对撞双指针:两个指针相向而行进行碰撞
- 快慢双指针:快指针移动快一点,慢指针移动慢一点
移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
自己的题解:
cpp
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int index1=0,index2=0;
while(index1<nums.size()&&index2<nums.size())
{
if(nums[index1]==0) //为0就用index2去找非零去进行交换
{
index2=index1+1; //后一个元素开始寻找,会导致重复寻找浪费时间
while(index2<nums.size()&&nums[index2]==0) index2++;
//交换
if(index2<nums.size()) //er:数组越界
{
nums[index1]=nums[index2];
nums[index2]=0; //er:注意不要重置index2,继续扫描
}
}
index1++;
}
}
};
我的思路:就是利用两个指针 index1 和 index2去进行查找,首先让 index1 查找0的位置方便后续交换,之后就用 index2 去查找非0的位置进行交换。就是将两个指针的职责分开一个找0,一个找非0。
官方题解
cpp
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int n = nums.size(), left = 0, right = 0;
while (right < n) {
if (nums[right]) {
swap(nums[left], nums[right]);
left++;
}
right++;
}
}
};
官方的解题思路则是 right 找扫描数组找非0,然后直接把非0的元素直接和 left位置进行交换,就是把非0的元素前移自然而然的0都会被放到最后末尾。这个很好的一点就是让 right 继续往右开始不用重新赋值节约时间。
盛水最多的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。

输入: [1,8,6,2,5,4,8,3,7]
**输出:**49
自己的题解:
cpp
class Solution {
public:
int maxArea(vector<int>& height) {
int right=height.size()-1; //左右指针
int left=0;
int target=0;
while(left<right)
{
int min=height[left]<height[right]?height[left]:height[right];
if(min*(right-left)>target) //判断大小
{
target=min*(right-left);
}
if(height[left]>height[right]) //移动指针
{
right--;
}else
{
left++;
}
}
return target;
}
};
我的思路:就是首先使用对撞双指针,主要是去表示最小的容量怎么表示,底*高进行表示容量,最重要的就是指针的移动要选取小的边进行移动去找到更大的高度。
官方题解
cpp
class Solution {
public:
int maxArea(vector<int>& height) {
int l = 0, r = height.size() - 1;
int ans = 0;
while (l < r) {
int area = min(height[l], height[r]) * (r - l);
ans = max(ans, area);
if (height[l] <= height[r]) {
++l;
}
else {
--r;
}
}
return ans;
}
};
官方的思路也是一致的都是通过对撞双指针去进行实现。
三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
**注意:**答案中不可以包含重复的三元组。
**输入:**nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
自己的题解
cpp
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>>tag;
sort(nums.begin(),nums.end()); //排序
int left=0,right=0;
for(int i=0;i<nums.size()-2;i++) //留出i+1防止溢出
{
if(i>0&&nums[i]==nums[i-1]) //er:处理重复值
{
continue;
}
right=nums.size()-1; //直接赋值
left=i+1;
while(left<right)
{
if(nums[i]+nums[left]+nums[right]<0)
{
left++;
}else if(nums[i]+nums[left]+nums[right]>0)
{
right--;
}else //er:等于0时的去重逻辑
{
tag.push_back({nums[i], nums[left], nums[right]});
// 记录当前值,用来跳过重复
int lval = nums[left], rval = nums[right];
// 左右都跳过重复值
while (left < right && nums[left] == lval) left++;
while (left < right && nums[right] == rval) right--;
}
}
}
return tag;
}
};
我的思路就是先对这个 nums 进行sort排序,之后就可以通过与 0 进行比较进行移动指针,其中最重要的就是对于去重操作
- 第一处去重就是选择
nums[i]时需要去重 - 第二处就是
nums[left]和nums[right]需要去重
注意掌握去重操作就行。
官方题解
cpp
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int>> ans;
// 枚举 a
for (int first = 0; first < n; ++first) {
// 需要和上一次枚举的数不相同
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
// c 对应的指针初始指向数组的最右端
int third = n - 1;
int target = -nums[first];
// 枚举 b
for (int second = first + 1; second < n; ++second) {
// 需要和上一次枚举的数不相同
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// 需要保证 b 的指针在 c 的指针的左侧
while (second < third && nums[second] + nums[third] > target) {
--third;
}
// 如果指针重合,随着 b 后续的增加
// 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
if (second == third) {
break;
}
if (nums[second] + nums[third] == target) {
ans.push_back({nums[first], nums[second], nums[third]});
}
}
}
return ans;
}
};
官方的思路也是先对数组进行重排,然后同样是固定a,然后b从左到右扫,c从右往左扫。我们的本质思路是一样的都是利用对撞双指针进行向中间靠,然后固定一个数去与进行比较移动指针。
int target = -nums[first];官方是直接把这个0-固定数进行判断,我则是直接三数和0进行判断,本质是一样的。
接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

**输入:**height = [0,1,0,2,1,0,1,3,2,1,2,1]
**输出:**6
自己的题解
cpp
#include <vector>
using namespace std;
class Solution {
public:
int trap(vector<int>& height) {
int num=height.size();
if(num<3) return 0;
int count=0;
int index1=0,index2=num-1; //对撞
int left_max=0,right_max=0;
while(index1<index2)
{
int temp_max=0; //中间最小
//边界最小
int index_minval=height[index1]>height[index2]?height[index2]:height[index1];
//边界最大
int index_maxval=height[index1]>height[index2]?height[index1]:height[index2];
int max_index=0,min_index=0; //指针大小
if(height[index1] <= height[index2])
{
if(height[index1] >= left_max) left_max = height[index1];
else count += left_max - height[index1];
index1++;
}
else
{
if(height[index2] >= right_max) right_max = height[index2];
else count += right_max - height[index2];
index2--;
}
}
return count;
}
};
我的思路:就是通过对撞双指针进行的,因为水桶效应装水量取决于矮边 ,然后在去矮的一边进行取最大的max值进行减去当前的一个height值就是这一个height[i]能取到的水值,之后再移动矮的一边,因为不确定这边是不是还有比它更高的;同理右边也是一样的。(当然我最开始的思路是错的,这也是让AI提示思路之后的代码😭)
官方题解
cpp
class Solution {
public:
int trap(vector<int>& height) {
int ans = 0;
int left = 0, right = height.size() - 1;
int leftMax = 0, rightMax = 0;
while (left < right) {
leftMax = max(leftMax, height[left]);
rightMax = max(rightMax, height[right]);
if (height[left] < height[right]) {
ans += leftMax - height[left];
++left;
} else {
ans += rightMax - height[right];
--right;
}
}
return ans;
}
};
当然官方的题解也是这样的,采用对撞双指针 加水桶效应矮边进行解题。