【算法题解答·二】双指针法

【算法题解答·二】双指针法

接上文 【算法方法总结·二】双指针的一些技巧和注意事项


双指针法相关题目如下:

27.移除元素 简单

  • 此题 两种写法均可
  • 但要求中有原地移除,根据上文技巧的内容,选 快慢指针法更优
class Solution {
    public int removeElement(int[] nums, int val) {
        int len = nums.length;
        // slow,fast 均初始化为 0 
        int slow = 0;
        for (int fast = 0; fast < len; fast++) {
        	// fast来寻找新数组,slow用来更新数组
            if (nums[fast] != val) {
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow; // 返回新数组的个数
    }
}

977.有序数组的平方 简单

  • 数组有序,根据上文技巧的内容,选 相向双指针法
  • 数组 平方最大值数组的两端,所以指针从两端往中间走
class Solution {
    public int[] sortedSquares(int[] nums) {
    	// 双向双指针法
        int left = 0, right = nums.length - 1;
        int[] result = new int[nums.length];
        int index = nums.length - 1; // 最大的存在 result 数组最后
        while (left <= right) {
            if (nums[left] * nums[left] > nums[right] * nums[right]) {
                result[index--] = nums[left] * nums[left];
                ++left;
            } else { // nums[left] * nums[left] <= nums[right] * nums[right]
                result[index--] = nums[right] * nums[right];
                --right;
            }
        }
        return result;
    }
}

283.移动零 简单

  • 要求中有原地操作,根据上文技巧的内容,选 快慢指针法更优
  • 此题与上述 27.移除元素 解法几乎一样
// 用 slow,fast 双指针,快指针 fast 遍历到不为 0 的元素往前移到慢指针 slow 的位置上
class Solution {
    public void moveZeroes(int[] nums) {
        int len = nums.length;
        int slow = 0;
        for (int fast = 0; fast < len; fast++) {
            if (nums[fast] != 0) {
                nums[slow] = nums[fast];
                slow++;
            }
        }
        for (int i = slow; i < len; i++) {
            nums[i] = 0;
        }
    }
}

11.盛最多水的容器

  • 两端协调问题,根据上文技巧的内容,选 相向双指针法
  • 木桶原理,装水容量受最短木板影响
class Solution {
    public int maxArea(int[] height) {
   	 	// 双向双指针法
        int left = 0, right = height.length - 1;
        int ans = 0; // 容量(面积)
        // 为啥不是 left <= right,因为 left == right 时容量为0,没意义
        while (left < right) {
        	// 木桶原理,装水容量受最短木板影响,所以取 min
            int area = (right - left) * Math.min(height[left], height[right]);
            ans = Math.max(ans, area);
            // 受最短木板影响,尽量留下长的那块木板,所以 left 不动,right--
            if (height[left] > height[right]) {
                right--;
            } else { // 尽量留下长的那块木板,所以 right 不动,left++
                left++;
            }
        }
        return ans;
    }
}

15.三数之和

  • 三个数 分别设为 i,j,k,首先要保证有序,就能用相向双指针法来降低时间复杂度了
  • 本题还需要加一个去重操作
// 先遍历第一个元素 i,然后让 j = i+1,k = 最后一个元素,j,k为相向双指针
// 记得去重 
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        Arrays.sort(nums); // 保证有序
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > 0) return ans;
            // 去重 i
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            int j = i + 1;
            int k = nums.length - 1;
            // j,k 相向双指针
            while (j < k) {
                int sum = nums[i] + nums[j] + nums[k];
                if (sum > 0) {
                    k--;
                } else if (sum < 0) {
                    j++;
                } else { // sum==0
                    ans.add(Arrays.asList(nums[i], nums[j], nums[k]));
                    // 去重 j,k
                    while (j < k && nums[j] == nums[j + 1]) j++;
                    while (j < k && nums[k] == nums[k - 1]) k--;
                    j++;
                    k--;
                }
            }
        }
        return ans;
    }
}

42.接雨水 困难

  • 听说面试经常考,得背熟
  • 此题可以用单调栈的方法做,但这里只考虑相向双指针的方法
  • 相向双指针法是对暴力法的优化,暴力法按下图所示一列一列计算,会超时
  • 每到一个柱子都向两侧遍历一遍,这其实是有重复计算的,把每一个位置的左边最高高度记录在一个数组上(maxLeft),右边最高高度记录在一个数组上(maxRight),这样就避免了重复计算
    在这里插入图片描述
// 按列计算值
class Solution {
    public int trap(int[] height) {
        int len = height.length;
        if (len <= 2) { 
            return 0;
        }
        // 相向双指针法,maxLeft,maxRight 跟着 l,r 的变化而变化
        int maxLeft = height[0], maxRight = height[len - 1];
        int l = 1, r = len - 2; // 两端下标 0 和 len-1 处无法蓄水
        int res = 0;
        while (l <= r) {
            // 不管移动哪边,都更新
            maxLeft = Math.max(maxLeft, height[l]);
            maxRight = Math.max(maxRight, height[r]);
            // 较小的一边先确定量值
            if (maxLeft < maxRight) {
                res += maxLeft - height[l];
                l++;
            } else {
                res += maxRight - height[r];
                r--;
            }
        }
        return res;
    }
}

算法题解答系列

【算法题解答·一】二分法
【算法题解答·二】双指针法
【算法题解答·三】滑动窗口
【算法题解答·四】字符串操作
【算法题解答·五】链表操作
【算法题解答·六】栈队列堆

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值