题目列表
11. 盛最多水的容器
42. 接雨水
15. 三数之和
16. 最接近的三数之和
18. 四数之和
26. 删除有序数组中的重复项
27. 移除元素
75. 颜色分类
167. 两数之和 II - 输入有序数组
2024.04.06
11. 盛最多水的容器
题目
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明: 你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
提示:
- n == height.length
- 2 <= n <= 105
- 0 <= height[i] <= 104
思路
- 从图可知,接水量=Math.min(left, right) * (rightIndex - leftIndex)
- 可以用双指针
- 因为要找最大值,而且双指针相向而行(宽度在变小),只有最小高度变大才可能有最大值,所以移动高度相对较低的一端
答案
class Solution {public int maxArea(int[] height) {int length = height.length;if (length < 2) {return 0;}int left = 0;int right = length - 1;int leftH = height[left];int rightH = height[right];int result = 0;while (left < right) {int h = Math.min(leftH, rightH);result = Math.max(result, h * (right - left));if (leftH == h) {left++;// 此处快速收敛一下,因为leftH >= height[left],就不可能更大while (left < right && leftH >= height[left]) {left++;}leftH = height[left];} else {right--;// 同left处while (left < right && rightH >= height[right]) {right--;}rightH = height[right];}}return result;}
}
42. 接雨水
题目
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 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 个单位的雨水(蓝色部分表示雨水)。
示例 2:
输入:height = [4,2,0,3,2,5]
输出:9
提示:
- n == height.length
- 1 <= n <= 2 * 104
- 0 <= height[i] <= 105
思路
和上一题的区别是,上一题认为每个位置的高度都是0,本题是有一个基础高度
所以不能简单的用high * length,要逐个计算高度
整体思路和上一题类似,区别是每个格子能盛水的量是,Math.min(左max,右max)
答案
class Solution {public int trap(int[] height) {int length = height.length;if (length < 2) {return 0;}int left = 0;int right = length - 1;int leftHMax = height[left];int rightHMax = height[right];int result = 0;// 因为计算高度在判断后进行。判断后会进行一次位移,left和right会重合,这里不需要left <= rightwhile (left < right) {int minMax = Math.min(leftHMax, rightHMax);if (minMax == leftHMax) {left++;if (height[left] < minMax) {result += minMax - height[left];} else {leftHMax = height[left];}} else {right--;if (height[right] < minMax) {result += minMax - height[right];} else {rightHMax = height[right];}}}return result;}
}
15. 三数之和
题目
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
示例 1:
输入: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] 。
注意,输出的顺序和三元组的顺序并不重要。
示例 2:
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。
示例 3:
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。
提示:
- 3 <= nums.length <= 3000
- -105 <= nums[i] <= 105
思路
- 循环每一个数,然后变为newTarget = target - nums[i]的两数之和
- 排序之后,循环每一个数,然后双指针。
- newTarget = target - nums[i]。left + right = newTarget。
- 因为排序,left向右则和变大,right向左则和变小
- 综上,用双指针
答案
class Solution {public List<List<Integer>> threeSum(int[] nums) {int length = nums.length;sorted(nums, 0, length - 1);List<List<Integer>> result = new ArrayList<>();for (int i = 0; i < length - 1; i++) {if (nums[i] > 0) {return result;}// 去除重复元素if (i > 0 && nums[i] == nums[i - 1]) {continue;}int target = -nums[i];int left = i + 1;int right = length - 1;while (left < right) {int sum = nums[left] + nums[right];if (sum == target) {result.add(Arrays.asList(nums[i], nums[left], nums[right]));// 去除重复元素,这里用left和left+1对比,而不是用left和left-1对比,是为了后续移动left统一操作while (left < right && nums[left] == nums[left+1]) {left++;}// 去除重复元素while (left < right && nums[right] == nums[right - 1]) {right--;}left++;right--;} else if (sum < target) {left++;} else {right--;}}}return result;}// 顺便练习下,有重复元素的快排(颜色分类用到了)public void sorted(int[] nums, int left, int right) {if (left >= right) {return;}// min指向小于element元素的下一个值(不包含)[left, mid)是小于element的// mid指向当前遍历元素[min, mid)是和element相等的值// max是指向第一个比element大的元素的左边的元素,(max, right] 比element大// [mid, max]是未遍历元素int min = left, mid = min + 1, max = right;int element = nums[min];while (mid <= max) {int current = nums[mid];if (element == current) {mid++;} else if (element < current) {swap(nums, mid, max--);} else {swap(nums, min++, mid++);}}//因为min和max都是开区间,所以,这里做一个位移sorted(nums, left, min - 1);sorted(nums, max + 1, right);}private void swap(int[] nums, int index1, int index2) {int temp = nums[index1];nums[index1] = nums[index2];nums[index2] = temp;}
}
2024.04.07
16. 最接近的三数之和
题目
给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。
示例 1:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
示例 2:
输入:nums = [0,0,0], target = 1
输出:0
提示:
- 3 <= nums.length <= 1000
- -1000 <= nums[i] <= 1000
- -104 <= target <= 104
思路
同上三数之和,可以作为练习题
答案
class Solution {public int threeSumClosest(int[] nums, int target) {int length = nums.length;int result = 0;sorted(nums, 0, length - 1);int min = Integer.MAX_VALUE;for (int i = 0; i < length; i++) {int nums1 = nums[i];int left = i + 1;int right = length - 1;while (left < right) {int sum = nums1 + nums[left] + nums[right];// 判断差值if (Math.abs(sum - target) < min) {min = Math.abs(sum - target);result = sum;}// 找到直接截断if (sum == target) {return sum;} else if (sum < target) {left++;} else {right--;}}}return result;}private void sorted(int[] nums, int left, int right) {if (left >= right) {return;}int element = nums[left];int min = left, mid = min + 1, max = right;while (mid <= max) {int current = nums[mid];if (current == element) {mid++;} else if (current < element) {swap(nums, min++, mid++);} else {swap(nums, mid, max--);}}sorted(nums, left, min - 1);sorted(nums, max + 1, right);}private void swap(int[] nums, int index1, int index2) {int temp = nums[index1];nums[index1] = nums[index2];nums[index2] = temp;}
}
18. 四数之和
题目
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
- 0 <= a, b, c, d < n
- a、b、c 和 d 互不相同
- nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]
提示:
- 1 <= nums.length <= 200
- -109 <= nums[i] <= 109
- -109 <= target <= 109
思路
和三数之和类似
区别是
- 需要两次循环
- length条件不一样
- nums[i]的条件不一样,可能相加之后溢出
答案
class Solution {public List<List<Integer>> fourSum(int[] nums, int target) {List<List<Integer>> result = new ArrayList<>();int length = nums.length;if (length < 4) {return result;}sorted(nums, 0, length - 1);for (int i = 0; i < length - 3; i++) {long first = nums[i];// 快速截断,已经超过目标值了if (first + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {break;}// 快速截断,不可能达成if (first + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {continue;}if (i > 0 && nums[i] == nums[i - 1]) {continue;}for (int j = i + 1; j < length - 2; j++) {// 同上if (first + nums[j] + nums[j + 1] + nums[j + 2] > target) {break;}// 同上if (first + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {continue;}if (j > i + 1 && nums[j] == nums[j - 1]) {continue;}int second = nums[j];int left = j + 1;int right = length - 1;while (left < right) {long sum = first + second + nums[left] + nums[right];if (sum == target) {result.add(Arrays.asList((int) first, second, 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 < target) {left++;} else {right--;}}}}return result;}private void sorted(int[] nums, int left, int right) {if (left >= right) {return;}int element = nums[left];int min = left, mid = min + 1, max = right;while (mid <= max) {int current = nums[mid];if (current == element) {mid++;} else if (current < element) {swap(nums, min++, mid++);} else {swap(nums, mid, max--);}}sorted(nums, left, min - 1);sorted(nums, max + 1, right);}private void swap(int[] nums, int index1, int index2) {int temp = nums[index1];nums[index1] = nums[index2];nums[index2] = temp;}
}
26. 删除有序数组中的重复项
题目
给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:
- 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
- 返回 k 。
判题标准:
系统会用下面的代码来测试你的题解:
int[] nums = […]; // 输入数组
int[] expectedNums = […]; // 长度正确的期望答案
int k = removeDuplicates(nums); // 调用
assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
assert nums[i] == expectedNums[i];
}
如果所有断言都通过,那么您的题解将被 通过。
示例 1:
输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
示例 2:
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。
提示:
- 1 <= nums.length <= 3 * 104
- -104 <= nums[i] <= 104
- nums 已按 非严格递增 排列
思路
- 一个size记录新的大小
- 如果下一个元素和当前元素相等,size就不动
- 否则size+1,并把当前位置的元素复制过来
答案
class Solution {public int removeDuplicates(int[] nums) {int size = 0;// 看提示,可以确认大小范围int last = -100000;for (int i = 0; i < nums.length; i++) {if (nums[i] == last) {continue;} else {nums[size++] = nums[i];last = nums[i];}}return size;}
}
27. 移除元素
题目
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
示例 1:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,3,0,4]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
提示:
- 0 <= nums.length <= 100
- 0 <= nums[i] <= 50
- 0 <= val <= 100
思路
和上一题相同。区别是,size变更的条件
上一题是last和当前不相等
这个一题是val和当前不相等
答案
class Solution {public int removeElement(int[] nums, int val) {int size = 0;for (int i = 0; i < nums.length; i++) {int current = nums[i];if (current == val) {continue;} else {nums[size++] = nums[i];}}return size;}
}
75. 颜色分类
题目
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
必须在不使用库内置的 sort 函数的情况下解决这个问题。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:
输入:nums = [2,0,1]
输出:[0,1,2]
提示:
- n == nums.length
- 1 <= n <= 300
- nums[i] 为 0、1 或 2
进阶:
- 你能想出一个仅使用常数空间的一趟扫描算法吗?
思路
重复元素排序
元素
class Solution {public void sortColors(int[] nums) {sorted(nums, 0, nums.length - 1);}private void sorted(int[] nums, int left, int right) {if (left >= right) {return;}int element = nums[left];int min = left, mid = min + 1, max = right;while (mid <= max) {int current = nums[mid];if (element == current) {mid++;} else if (current < element) {swap(nums, min++, mid++);} else {swap(nums, mid, max--);}}sorted(nums, left, min - 1);sorted(nums, max + 1, right);}private void swap(int[] nums, int index1, int index2) {int temp = nums[index1];nums[index1] = nums[index2];nums[index2] = temp;}
}
167. 两数之和 II - 输入有序数组
题目
给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。
以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。
示例 1:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
示例 2:
输入:numbers = [2,3,4], target = 6
输出:[1,3]
解释:2 与 4 之和等于目标数 6 。因此 index1 = 1, index2 = 3 。返回 [1, 3] 。
示例 3:
输入:numbers = [-1,0], target = -1
输出:[1,2]
解释:-1 与 0 之和等于目标数 -1 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
提示:
- 2 <= numbers.length <= 3 * 104
- -1000 <= numbers[i] <= 1000
- numbers 按 非递减顺序 排列
- -1000 <= target <= 1000
- 仅存在一个有效答案
思路
三数之和的简单版本
答案
class Solution {public int[] twoSum(int[] numbers, int target) {int l = 0, r = numbers.length - 1;while (l < r) {int current = numbers[l] + numbers[r];if (current > target) {r--;} else if (current < target) {l++;} else {// 题目要求从1开始return new int[] { l + 1, r + 1 };}}return new int[2];}
}