昨天寫到 two number sum 的兩種解法,一種使用兩層迴圈,時間為 O(n^2);另一種使用 hash table,時間為 O(n)。這裡先寫第三種解法,時間表現雖然介於前兩者之間,但很簡潔好懂,也很適合延伸用來解 three number sum 問題。
這個解法是利用有序陣列的特性加上指標 (pointers) 來解題。
例如輸入為 [3, 5, -4, 8, 11, 1, -1, 6], 10
步驟:
n 為陣列長度
time: O(nlog(n)) 這個時間是因排序而來 (取正常發揮的排序演算法時間),排序後只有進行常數時間運算所以忽略掉。
space: O(1)
function twoNumberSum(array, target) {
array.sort((a, b) => a - b);
let left = 0;
let right = array.length - 1;
while (left < right) {
const sum = array[left] + array[right];
if (sum === target) {
return [array[left], array[right]];
} else if (sum < target) {
left++;
} else {
right--;
}
}
return [];
}
以上面的解法為基礎,接著看 three number sum 問題。
輸入為一陣列及一整數 target,如果陣列中有三個數字相加等於 target,以雙層陣列 (two-dimentional array) 的結構回傳所有數組,且所有數組要從小到大排序,否則回傳空陣列。
可以預設輸入陣列不為空陣列,且元素不重複。
sample input:
array = [12, 3, 1, 2, -6, 5, -8, 6]
target = 0
sample output:
[[-8, 2, 6], [-8, 3, 5], [-6, 1, 5]]
延續上一題的解法,稍微複雜一點,除了排序、指標外還用到迴圈。
步驟:
值得注意的是,不同於 two number sum 一找到解就可以回傳結果,這題可能有不只一組解,因此也多了找到解後,需要同時將指標都向內移動的這種情況。
n 為陣列長度
time: O(n^2) 迴圈 + 每次迴圈又用指標看過所有數字一次。
space: O(n) 輸出陣列可能會放入 n 個數字。
function threeNumberSum(array, target) {
array.sort((a, b) => a - b);
const results = [];
// 迴圈只到倒數第三位,後面留兩個位置給指標
for (let i = 0; i < array.length - 2; i++) {
let left = i + 1;
let right = array.length - 1;
while (left < right) {
const sum = array[i] + array[left] + array[right];
if (sum === target) {
results.push([array[i], array[left], array[right]]);
left++;
right--;
} else if (sum < target) {
left++;
} else if (sum > target) {
right--;
}
}
}
return results;
}
如果從 three number sum 再進階到 four number sum,一切就會變得更複雜,希望...完賽前有機會整理成筆記,明天先寫其他的陣列題目。