---
title: 【LeetCode】0016. 3Sum Closest
date: 2019-06-11
is_modified: false
disqus: cynthiahackmd
categories:
- "面試刷題"
tags:
- "LeetCode"
---
{%hackmd @CynthiaChuang/Github-Page-Theme %}
<br>
Given an array `nums` of _n_ integers and an integer `target`, find three integers in `nums` such that the sum is closest to `target`. Return the sum of the three integers. You may assume that each input would have exactly one solution.
<!--more-->
<br>
**Example 1:**
```
Given array nums = [-1, 2, 1, -4], and target = 1.
The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
```
<br>
**Related Topics:** `Array`、`Two Pointers`
## 解題邏輯與實作
這基本上是 [3Sum](/XXy3Mz4DToygP2t_0ljjLA) 的變形,唯一不同的不再是回傳陣列,而是回傳最接近的總和。另外數字相同應該可以不用管,所以我先把它拔掉了。
```python=
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
assert len(nums) >= 3 , " n >= 3"
diff = 2**32
ans = 0
length = len(nums)
nums.sort()
for first_idx, first in enumerate(nums[:-2]):
if first_idx > 0 and first == nums[first_idx-1]:
continue
second_idx = first_idx + 1
third_idx = length -1
while(second_idx < third_idx):
second = nums[second_idx]
third = nums[third_idx]
summation = first + second + third
if abs(target-summation) < diff:
diff = abs(target-summation)
ans = summation
if summation < target:
second_idx += 1
elif summation > target:
third_idx -= 1
else:
break
return ans
```
不過這份程式碼的效能有點差,只跑出了 **176 / 27.85%** 的成績,所以我這邊又加上了一些邊界的判斷進行剪枝,加速迴圈的進行。
<br><br>
這邊加上了兩個條件
1. **在固定首位數字後與剩餘數列中頭兩個數字,計算三數之和。若和大於 target ,則與之前記錄下來答案相比,回傳最接近的 target 的值。**
<br>這是因為在陣列已排序且首位數字固定的情況下,剩餘數列中頭兩個數字和理應會是最小;且在尋訪的過程中,首位數字會逐漸加大,所以一旦出現首位數字與剩餘數列中頭兩個數字和大於 target 的狀況,其後的和皆會大於 target,且差值會愈來越大。因此一旦出現這狀況,即可停止尋訪。
<br>
2. **在固定首位數字後與剩餘數列中末兩個數字,計算三數之和。若和小於 target ,直接向下尋訪下一個首位數字。**
<br>這是因為在陣列已排序且首位數字固定的情況下,剩餘數列中末兩個數字之和理應是最大,因此若此值小於 target,其他和也會小於 target,且差值大於此值。因此僅需記錄目前的狀況後,直接尋訪下一個首位數字即可,無須在考慮目前首位數字的其他組合。
```python=
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
assert len(nums) >= 3 , " n >= 3"
diff = 2**32
ans = 0
length = len(nums)
nums.sort()
for first_idx, first in enumerate(nums[:-2]):
if first_idx > 0 and first == nums[first_idx-1]:
continue
summation = first + nums[first_idx+1] + nums[first_idx+2]
if summation > target:
return summation if abs(target- summation) < diff else ans
summation = first + nums[length-2] + nums[length-1]
if summation < target:
if abs(target- summation) < diff :
diff = abs(target-summation)
ans = summation
continue
second_idx = first_idx + 1
third_idx = length -1
while(second_idx < third_idx):
second = nums[second_idx]
third = nums[third_idx]
summation = first + second + third
if summation == target:
return summation
elif summation < target:
second_idx += 1
else:
third_idx -= 1
if abs(target-summation) < diff:
diff = abs(target-summation)
ans = summation
return ans
```
改良的效果有點出乎意料,比我想像好很多從 **176 / 27.85%** 提升到 **28 / 100%** 。
## 其他連結
1. [【LeetCode】0000. 解題目錄](/x62skqpKStKMxRepJ6iqQQ)
<br><br>
> **本文作者**: 辛西亞.Cynthia
> **本文連結**: [辛西亞的技能樹](https://cynthiachuang.github.io/LeetCode-0016-3Sum-Closest) / [hackmd 版本](https://hackmd.io/@CynthiaChuang/LeetCode-0016-3Sum-Closest)
> **版權聲明**: 部落格中所有文章,均採用 [姓名標示-非商業性-相同方式分享 4.0 國際](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en) (CC BY-NC-SA 4.0) 許可協議。轉載請標明作者、連結與出處!