02.双指针,启动!
移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
- 方法1:
class Solution {
public void moveZeroes(int[] nums) {
int i = 0;
int j = 0;
//将所有非0元素复制
for (; i < nums.length; i++) {
if(nums[i]!=0){
nums[j] = nums[i];
j++;
}
}
//后续全部补为0
for(;j<nums.length;j++){
nums[j]=0;
}
}
}
-
方法2:
遍历过程中动态记录非0元素个数k,将非0向前移动k步,并在后续补0.(非双指针) -
延申
题目让我想起了荷兰旗问题,以及快排的partition过程,也是经典的双指针问题。
盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
- 方法:
头尾指针向中间移动(宽度逐渐减小),谁高度小谁移动,在移动过程中,若变大了,则动态记录当前最大容量,当头尾指针相遇即得到结果。
class Solution {
public int maxArea(int[] height) {
int l=0;
int r=height.length-1;
if (height.length<=1) {return 0;}
int h = Math.min(height[l], height[r]);
int area = h*(r-l);
while(l<r){
if (height[l]<height[r]) l++;
else r--;
if(Math.min(height[l], height[r]) > h) {
h = Math.min(height[l], height[r]);
area = Math.max(area, h * (r - l));
}
}
return area;
}
}
三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
- 方法
三数之和为0,即让另外两数之和与当前数为相反数。先对数组进行排序,再依次遍历nums,寻找当前遍历数nums[i]后续的有序数组中和为-nums[i]的两个值。(利用双指针从两边向中间找)
又因为不能包含重复的结果,则要采取一些去重操作:
1.因为数组以有序,则对于遍历nums过程中重复的数据(i > 0 && nums[i]==nums[i-1]
)是无需进行处理的,跳过即可。
2.在创建三元组的过程中亦会出现三元组重复的状况,所以在双指针向中间移动的过程中,也要跳过重复元素。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length-2; i++) {
if( i > 0 && nums[i]==nums[i-1]){
continue;
}
int target = -nums[i];
int r = nums.length-1;
for (int l = i+1; l < r; l++) {
//寻找nums[i]后续的有序数组中和为-nums[i]的两个值。
while(l<r &&nums[l]+nums[r]>target){
r--;
}
if(l == r){
break;
}
if(target == nums[l]+nums[r]){
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[l]);
list.add(nums[r]);
res.add(list);
//去重操作2
while(l<r&&nums[l]==nums[l+1]) {l++;}
while(l<r&&nums[r]==nums[r-1]) {r--;}
r--;
}
}
}
return res;
}
}
接雨水!
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
- 方法1 按列计算
每一列的存水量即为min(左边列最大高度,右边列最大高度)- 当前列高度,但如果直接记录每一列的左右最大高度需要O(n)的空间复杂度以及O(n^2)的时间复杂度。
利用双指针,我们无需知道其左右的最大高度分别是多少,只需要知道一边的最大高度是多少,并且确定另一边存在>=该值的边即可计算当前列的存水量。
1.双指针两边向中间移动(l,r),并动态计录当前遍历到的左最大值lmax及右最大值rmax
2.若lmax<rmax,l向右移动,并尝试更新lmaxlmax=Math.max(lmax,height[l])
,并计算当前列能提供的雨水量res+=lmax-height[l]
因为更新前的lmax<rmax,所以如果当前列>更新前的lmax,则lmax成功更新,此时当前列不会存储雨水,而lmax-height[l]
的结果也为对应的0
如果当前列<更新前的lmax,则lmax不会更新,即当前列出现凹槽,会提供雨水,而又因为lmax<rmax,所以其存水量即为lmax-height[l],而无需了解当前列的右边最大列高度
class Solution {
public int trap(int[] height) {
int l=0;
int r=height.length-1;
int lmax=height[l];
int rmax=height[r];
int res=0;
while(l<r){
if(lmax<rmax){
l++;
lmax=Math.max(lmax,height[l]);
res+=lmax-height[l];
}else {
r--;
rmax=Math.max(height[r], rmax);
res+=rmax-height[r];
}
}
return res;
}
}
- 方法2
题目也可以利用单调栈来解决,但是此为双指针专题,暂不做实现。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构