- 重新調整從 Grind 75 開始,主要還是用 kotlin, 不過因未來會用到 java, python 所以也來複習一下吧。
1.Two Sum
- Array/HashMap, Easy
- introduction
- 給定整數陣列 nums 和整數目標值 target,請返回兩個索引,使它們的數字和為 target。假設每個輸入只有一組解,且不能使用同一元素兩次。
- solution
- 用 HashMap 存儲數字與索引,一邊遍歷一邊檢查 target - nums[i] 是否已出現。
- 時間:O(n)
- 空間:O(n)
- kotlin
fun twoSum(nums: IntArray, target: Int): IntArray {
val map = mutableMapOf<Int, Int>()
for ((i, num) in nums.withIndex()) {
val complement = target - num
if (map.containsKey(complement)) {
return intArrayOf(map[complement]!!, i)
}
map[num] = i
}
return intArrayOf()
}
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[]{map.get(complement), i};
}
map.put(nums[i], i);
}
return new int[0];
}
def twoSum(nums, target):
num_map = {}
for i, num in enumerate(nums):
if target - num in num_map:
return [num_map[target - num], i]
num_map[num] = i
-
follow up
- 如果要找三數之和(3Sum)怎麼改?
- solution
- 目標是找到所有 nums[i] + nums[j] + nums[k] = 0(或指定 target)的三元組。
- 標準做法:先排序陣列,固定第一個數,再用雙指針找後兩個數。
- 時間:O(n²)
- 空間:O(1)(排序除外)
fun threeSum(nums: IntArray): List<List<Int>> {
nums.sort()
val res = mutableListOf<List<Int>>()
for (i in nums.indices) {
if (i > 0 && nums[i] == nums[i - 1]) continue
var l = i + 1
var r = nums.size - 1
while (l < r) {
val sum = nums[i] + nums[l] + nums[r]
when {
sum < 0 -> l++
sum > 0 -> r--
else -> {
res.add(listOf(nums[i], nums[l], nums[r]))
l++
r--
while (l < r && nums[l] == nums[l - 1]) l++
while (l < r && nums[r] == nums[r + 1]) r--
}
}
}
}
return res
}
- 如果陣列已經排序,怎麼用雙指針解法?
- solution
- 如果 nums 已排序,可以用左右指針從頭尾夾逼。
- 若 sum < target → 左指針右移
- 若 sum > target → 右指針左移
- 時間:O(n)
- 空間:O(1)
fun twoSumSorted(nums: IntArray, target: Int): IntArray {
var l = 0
var r = nums.size - 1
while (l < r) {
val sum = nums[l] + nums[r]
when {
sum == target -> return intArrayOf(l, r)
sum < target -> l++
else -> r--
}
}
return intArrayOf()
}
- 如果輸入很大,如何降低空間複雜度?
- 原本 HashMap 解法是 O(n) 空間。
- 若陣列已排序 → 使用 雙指針,O(1) 空間。
- 若未排序但空間受限 → 可先排序再雙指針,O(1) 空間但 O(n log n) 時間。
- 若不能改變原陣列 → 可複製一份排序(O(n) 空間),或用外部排序法處理。
方法 |
時間複雜度 |
空間複雜度 |
適用情況 |
HashMap |
O(n) |
O(n) |
未排序且追求 O(n) 時間 |
雙指針 (排序後) |
O(n log n) |
O(1) |
可改動陣列 |
雙指針 (複製排序) |
O(n log n) |
O(n) |
不能改動原陣列 |
題型 |
解法 |
核心思路 |
時間複雜度 |
空間複雜度 |
Kotlin 範例 |
Two Sum(未排序) |
HashMap |
儲存 值 -> 索引 ,邊走邊查 target - nums[i] 是否存在 |
O(n) |
O(n) |
map[target - num] 查找 |
Two Sum(已排序) |
雙指針 |
左右指針夾逼,如果 sum < target → 左移;sum > target → 右移 |
O(n) |
O(1) |
while(l<r) |
Two Sum(空間優化) |
排序+雙指針 |
先排序再雙指針,空間 O(1) 但時間 O(n log n) |
O(n log n) |
O(1) |
適用空間受限 |
3Sum |
排序+雙指針 |
固定一數,剩餘用雙指針找目標和 |
O(n²) |
O(1) |
需跳過重複數 |
3Sum(指定 target) |
排序+雙指針 |
同 3Sum,只是 target 改成任意值 |
O(n²) |
O(1) |
可套用到 4Sum |