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向右移动,并尝试更新lmax lmax=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
    题目也可以利用单调栈来解决,但是此为双指针专题,暂不做实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值