LeetCode0016-最接近的三数之和
题目:
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
示例:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
提示:
- 3 <= nums.length <= 10^3
- -10^3 <= nums[i] <= 10^3
- -10^4 <= target <= 10^4
分析:
解题思路
双指针法:
- 先让数组有序,也就是需要对数组进行排序
- 然后每次固定一个元素,再去寻找另外俩个元素,也就是双指针
双指针法的代码实现(代码:Solution01)
- 利用 Arrays.sort(nums) 对数组进行排序。
- 初始化一个用于保存结果的值 result = nusm[0] + nums[1] + nums[2] (不要自己设初值,直接从数组中抽取三个元素,假设这是最接近的三数之和,然后再更新就是了)。
- 利用下标 i 对数组进行遍历,此时就是在固定第一个元素,注意,下标 i 的边界为 i < nums.length-2,否则设置指针的时候会出现数组越界。
- 每次遍历的过程中设置两个指针,分别是 left = i + 1、right = nums.length - 1。
- 检查 sum = nums[i] + nums[left] + nums[right]与 target 的距离,如果该距离比之前保存的 result 与 target 的距离更小,就更新 result。
- 然后就是移动双指针。
- 如果 sum 的值比 target 大,那么我们让 right–,因为数组是有序的,right --会使得下一次的 sum 更小,也就更接近 target 的值
- 同理,如果 sum 的值比target 小,那么我们让 left++。·
- left++ 和 right-- 的界限自然是 left != right,如果 left == right,说明我们已经将所有的元素都遍历过一遍了。
重复上面的操作,直到i循环结束为止,返回 result。
双指针法的优化
解决元素重复问题(代码:Solution02)
- 举个例子,nums = [1,1,1,2,3] target = 7,那么最终的结果应该是 6 (1 + 2 + 3)。
- 但是按照上面的代码,在遍历的时候 nums[i]会重复的等于 1 这个数,但是其实之前 nums[i] 等于 1 已经遍历过了,后面的遍历都属于无用的遍历。
- 所以可以添加去重的操作
解决超越界限的问题(代码:Solution03)
- 举个例子,nums = [-3,-1,3,4,5]。
- 假设 i = 0,left = 1,right = 4,那么每次 left 和 right 之间都有许多元素,那么 left 和 right 之间的元素之和肯定也有一个最小值和一个最大值。
- 就如同 left = 1,right = 4,那么移动指针的情况下,nums[left] + nums[right] 的最小值肯定为 nums[left] + nums[left + 1],因为这两个元素是 left 和 right 范围内能取到的最小的两个元素,同理可证最大值。
- 如果 target 的值比 nums[i] + nums[left] + nums[left + 1] 的值还小,那么双指针无论怎么取,最后都会取到 nums[i] + nums[left] + nums[left + 1]。
- 同理可证 target 的值比nums[i] + nums[right] + nums[right - 1] 的值还大的情况。
- 所以可以增加一个判断,满足条件的情况下就可以直接取值,而不需要双指针一步步的判断来进行取值,减少了双指针的移动。
解决三数之和等于target的问题(代码:Solution04)
- 举个例子,nums = [1,1,2,3,4,5,6,10] target = 12,那么最终的结果应该是 12 (1 + 1 + 10)。
- 有些时候,可能会直接找到三数之和等于 target 的情况,此时直接返回结果即可,不需要在进行之后的循环,因为不可能有数比他自己更接近自己了。
代码:
import java.util.Arrays;
/**
* 0016-最近的三数之和
* 给定一个包括n个整数的数组nums和一个目标值 target 。找出nums中的三个整数,使得
* 它们的和与target最接近。返回这三个数的和。假定每组输入只存在唯一答案
* <p>
* 示例:
* <p>
* 输入:nums = [-1,2,1,-4], target = 1
* 输出:2
* 解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
* <p>
* 提示:
* <p>
* 3 <= nums.length <= 10^3
* -10^3 <= nums[i] <= 10^3
* -10^4 <= target <= 10^4
*/
/**
* 双指针法
*/
class Solution01 {
public int threeSumClosest(int[] nums, int target) {
// 对数组元素进行排序
Arrays.sort(nums);
// 初始化用于保存结果的值
int result = nums[0] + nums[1] + nums[2];
// 利用下标i对数组进行遍历
for (int i = 0; i < nums.length - 2; i++) {
// 每次遍历过程中,设置俩个指针
int left = i + 1;
int right = nums.length - 1;
// 俩个指针相遇,则循环结束
while (left != right) {
// 计算三个数之和
int sum = nums[i] + nums[left] + nums[right];
// 当前状态与target距离小于已经保存的最优状态,则更新result
if (Math.abs(sum - target) < Math.abs(result - target))