文章目录
- 前言
- 双指针
- 例题讲解
-
- [移动零 力扣](#移动零 力扣)
- [复写零 力扣](#复写零 力扣)
- [快乐数 力扣](#快乐数 力扣)
- [盛最多水的容器 力扣](#盛最多水的容器 力扣)
- [有效三角形的个数 力扣](#有效三角形的个数 力扣)
- [查找总价格为目标值的两个商品 力扣](#查找总价格为目标值的两个商品 力扣)
- [三数之和 力扣](#三数之和 力扣)
前言
在力扣校招算法题中,双指针技巧是一类高频且实用的解题方法。它并非真正的 "指针",而是通过两个数组下标(或迭代器)的协同移动,在数组划分、区间求解、环检测等场景中实现高效遍历与逻辑处理,往往能将时间复杂度从暴力法的 O(n平方)优化至O(n),是校招笔试和面试中突破数组类难题的关键武器。
本专栏将围绕力扣校招高频的双指针题型展开,从 "移动零""复写零" 的数组操作,到 "快乐数" 的环检测、"盛最多水的容器" 的区间优化,再到 "三数之和" 的多指针协同,逐一拆解双指针的核心逻辑、边界处理与去重技巧,帮助你建立 "看题辨双指针,提笔知如何移" 的解题思维,从容应对校招算法考察中的数组类挑战。
双指针
常用于:数组划分和数组分块
注意:这里的指针不是真的指针,是数组的下标
例题讲解
移动零 力扣
cur:从左往右遍历数组
dest:已处理区间内,非零元素的最后一个位置

cpp
代码展示:
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int dest = -1;
int cur = 0;
for(;cur<nums.size();cur++)
{
if(nums[cur] != 0)
{
swap(nums[dest+1],nums[cur]); dest++;
}
else{
;
}
}
}
};
复写零 力扣
注意:这题要求不要在超过该数组长度的位置写入元素
步骤:
一:先找到最后一个被复写的数
找法:1.先判断cur位置的值(cur放到下标0位置,dest放到下标-1位置)
2.决定dest向后移动多少步(注意是先移动再判断的) 3.判断一下dest是否已经到结束位置(等于或超过最后那个数的位置) 4.cur++5.如果dest超过最后那个数的位置
c++让最后那个位置等于0 再cur--;dest-=2二:从后向前完成复写操作
c++
引申:vector的size()-1就是最后一个位置的下标
区分元素和下标
区分==和=
注意:size()在用来表示下标的时候,建议赋值给int类型的之后再用
不然 eg:dest<a.size()-1的时候,dest会整形提升,如果是-1就惨了
cpp
代码展示:
class Solution {
public:
void duplicateZeros(vector<int>& arr) {
int cur = 0; int dest = -1;
for(;dest<(int)arr.size()-1;cur++)
{
if(arr[cur]==0) dest++;
dest++;
}
cur--;
if(dest == arr.size())
{
arr[arr.size()-1] = 0;
dest-=2;
cur--;
}
for(;cur>=0;cur--)
{
arr[dest] = arr[cur];
if(arr[cur] == 0) arr[--dest] = 0;
dest--;
}
}
};
快乐数 力扣
c++
这么说的话,那就只有可能为1或者无限循环(和无限不循环区分)--所以想到环
环的话用快慢双指针去解决
注意:快慢指针的起点都是n 快慢指针一定会在环入口相遇
引申:一定要动手模拟一下示例
cpp
代码展示:
class Solution {
public:
int algorithm(int p)
{
int sum = 0;
int q = 0;
while(p>=10)
{
q = p%10;
p/=10;
sum+=q*q;
}
sum+=p*p;
return sum;
}
bool isHappy(int n) {
int slow = n;
int fast = n;
slow = algorithm(n);
fast = algorithm(slow);
while(slow!=fast)
{
slow = algorithm(slow);
fast = algorithm(fast);
fast = algorithm(fast);
}
if(slow == 1) return true;
else return false;
}
};
盛最多水的容器 力扣
做法:left放在最左边,right放在最右边
比较完之后,看left和right哪个对应的值小些,就把哪个向另外一边靠近
cpp
代码展示:
class Solution {
public:
int maxArea(vector<int>& height) {
int cur = 0;
int dest = height.size()-1;
int max1 = 0;
while(cur!=dest)
{
max1 = max(max1,(dest-cur)*min(height[dest],height[cur]) ) ;
if(height[cur]<height[dest]) cur++;
else dest--;
}
return max1;
}
};
有效三角形的个数 力扣
相关数学知识: 三角形最小的那两边之和>最大那一边就可以构成三角形了
方法:先给数组排序,然后先固定最大的数,在最大的数的左边用双指针算法去找符合的数;然后再缩小最大的数...注意:如果
nums[left]+nums[right]>nums[c],那right-left就是第二大数下标为rgiht时的总个数,然后right--)注意区分
c和nums[c]!!!
cpp
代码展示:
class Solution {
public:
int triangleNumber(vector<int>& nums) {
sort(nums.begin(),nums.end());
int c = nums.size()-1;
int ret = 0;
while(c>=2)
{
int left = 0; int right = c-1;
while(left!=right)
{
if((nums[left]+nums[right])>nums[c])//记得加括号
{
ret+=right-left;
right--;
}
else left++;
}
c--;
}
return ret;
}
};
查找总价格为目标值的两个商品 力扣
这个题有单调性,用双指针正好(或者二分算法)--能用双指针肯定优先用双指针
注意:此题没说找不到怎么办,就不用管那种情况,但是!力扣要求所有路径都要有返回值,在最后加个return ...就行了,但是要是能转化为
vector<int>类型的,比如nullptr就不行
c++
引申:eg: return {1,1};可以被隐式转成vector<int>类型的(函数返回值是vector<int>的情况下)
cpp
代码展示:
class Solution {
public:
vector<int> twoSum(vector<int>& price, int target) {
vector<int>ret;
int left = 0;
int right = price.size()-1;
while(left!=right)
{
if(price[left]+price[right]>target) right--;
else if(price[left]+price[right]<target) left++;
else {
ret.push_back(price[left]);
ret.push_back(price[right]);
break;
}
}
return ret;
}
};
三数之和 力扣
这种和怎么样怎么样的一般都排序之后用双指针
这个题跟上面的有效三角形的个数有点像
细节问题:1.去重
left和right以及固定的那个数都要跳过重复元素(哪个跟哪个比较==才去要注意)--于此同时要避免越界,比如:
left一直要<right补充:当然也可以找出所有结果之后,用
unordered_set去重------可是,去面试的时候这两种方法都可能会问到2.不漏
找到一种结果之后,不能直接break出去,要eg:
left++;right--继续寻找
引申:迭代器和下标怎么确立关系:(下标为p)--迭代器连续的那种才行(eg:vector算,list不算)eg:
auto a = ret.begin()+p
逗号不能用来同时定义两个不同类型的变量eg:
int a = 1,double b = 0;是不行的
c++
引申:题目给的target不要直接拿来运算,不然后续想要原来的就难了
eg:vector<vector<int>>的取名叫vv很好
溢出问题很容易读题时考虑到,后面又忘了--比如应该写long long int 又写成了int
cpp
代码展示:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ret;
sort(nums.begin(),nums.end());
int i = 0,j = 0;
for(int k = nums.size()-1;k>=2;k--)
{
if(nums[k]<0) break;
i = 0;j = k-1;
while(i<j)
{
if(nums[i]+nums[j]+nums[k]>0) j--;
else if(nums[i]+nums[j]+nums[k]<0) i++;
else{
ret.push_back({nums[i],nums[j],nums[k]});
i++;j--;
//去重
while(nums[i] == nums[i-1]&&i<j) i++;
while(nums[j] == nums[j+1]&&i<j) j--;
}
}
while(nums[k] == nums[k-1]&&k>=2) k--;
}
return ret;
}
};
