您的当前位置:首页正文

【Java完整版 面试必备】Leetcode Top100题目和答案-双指针篇

2024-12-03 来源:个人技术集锦

以下摘自leetcode Top100精选题目-双指针篇


给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例:

输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

Solution:

可以通过双指针技术来解决,一个指针用于遍历数组,另一个指针用于记录当前非零元素应该放置的位置。

public class Solution {
    public void moveZeroes(int[] nums) {
        int nonZeroIndex = 0; // 指向下一个应放置非零元素的位置

        // 遍历数组
        for (int i = 0; i < nums.length; i++) {
            // 如果当前元素非零,将其移到nonZeroIndex指向的位置
            if (nums[i] != 0) {
                nums[nonZeroIndex] = nums[i];
                // 如果当前元素是0,并且我们刚刚移动了一个非零元素,
                // 则需要将nonZeroIndex向前推进
                if (i != nonZeroIndex) {
                    nums[i] = 0;
                }
                nonZeroIndex++;
            }
        }
    }
}

     先定义nonZeroIndex变量,用于记录下一个非零元素应该放置的位置。遍历数组nums,当遇到非零元素时,就将其与nums[nonZeroIndex]交换,之后nonZeroIndex向前推进。如果当前元素是0,但之前已经交换过非零元素(即i != nonZeroIndex),则直接将nums[i]置为0,因为在交换过程中已经有一个非零元素占据了正确位置。

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

示例:

输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

Solution:

public class Solution {
    public int maxArea(int[] height) {
        int left = 0; // 左指针
        int right = height.length - 1; // 右指针
        int maxWater = 0; // 初始化最大水量为0

        while (left < right) {
            // 计算当前左右指针能容纳的水量,取高度较小的一边乘以两指针间的距离
            int currentWater = Math.min(height[left], height[right]) * (right - left);
            maxWater = Math.max(maxWater, currentWater); // 更新最大水量

            // 移动高度较小的指针,因为减少较短边可以增加潜在的容器宽度,有可能找到更大的面积
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }

        return maxWater;
    }
}

     初始化左指针left为0,右指针right为数组最后一个元素的索引。在循环中,计算当前左右指针所能围成的容器的最大容积,并更新全局的最大容积maxWater。每次移动高度较小的那个指针,因为这样有机会找到更宽的底边,从而有可能形成更大的容器。循环直到左右指针相遇,最终返回maxWater作为结果。

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

Solution:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if (nums == null || nums.length < 3) {
            return result;
        }
        
        // 先排序,使问题转换为在有序数组中寻找和为0的三元组
        Arrays.sort(nums);
        
        for (int i = 0; i < nums.length - 2; i++) {
            // 跳过重复的元素
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            
            int left = i + 1, right = nums.length - 1;
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum == 0) {
                    // 找到一个和为0的三元组
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    
                    // 跳过重复的元素
                    while (left < right && nums[left] == nums[left + 1]) {
                        left++;
                    }
                    while (left < right && nums[right] == nums[right - 1]) {
                        right--;
                    }
                    
                    // 收缩窗口,继续寻找
                    left++;
                    right--;
                } else if (sum < 0) {
                    // 如果和小于0,增大左侧指针
                    left++;
                } else {
                    // 如果和大于0,减小右侧指针
                    right--;
                }
            }
        }
        
        return result;
    }
}

    先对数组进行排序,使用一个外层循环遍历数组,内层使用双指针(一个从当前元素的下一个元素开始,另一个从数组末尾开始)来寻找和为0的三元组。为了避免重复,当找到一个三元组后,需要跳过所有和当前指针指向值相同的元素。

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 

Solution:

     解决“接雨水”问题的一个常见方法是使用两个指针从两端向中间移动,同时维护两个变量来跟踪左右两侧的最大高度。这种方法基于这样一个事实:任何柱子能接的雨水量都受限于其两侧最高的柱子。

public class Solution {
    public int trap(int[] height) {
        if (height == null || height.length == 0) {
            return 0;
        }
        
        int left = 0, right = height.length - 1; // 初始化左右指针
        int maxLeft = 0, maxRight = 0; // 初始化左右两侧的最大高度
        int water = 0; // 初始化接雨水的总量
        
        while (left < right) {
            if (height[left] <= height[right]) {
                // 如果左边的柱子矮,那么左边的柱子能接的雨水量由它自己决定,但实际能接的雨水量受限于maxLeft
                water += Math.max(0, maxLeft - height[left]);
                maxLeft = Math.max(maxLeft, height[left]); // 更新左侧最大高度
                left++; // 左指针向右移动
            } else {
                // 同理,如果右边的柱子矮,更新右侧最大高度并向左移动指针
                water += Math.max(0, maxRight - height[right]);
                maxRight = Math.max(maxRight, height[right]);
                right--;
            }
        }
        
        return water;
    }
}

    先初始化左右指针、左右两侧的最大高度以及接雨水的总量。进入一个循环,直到左右指针相遇。在每一步中比较左右柱子的高度,并移动较矮的那一侧的指针。通过比较当前柱子高度与其侧的最大高度,可以计算出当前位置能接的雨水量,并累加到总水量中。 

显示全文