就個人而言,我是喜歡演算法的,奈何不擅長,所以選擇了我同樣喜歡的前端(我才不會告訴你是因為有好多漂亮妹子在呢)。
那麼今天來看下leetcode 第1題,兩數之和:
給你一個陣列,返回2個數之和為一個目標值的下標,輸入資料只會有準確的一種解法,同一個元素不能使用2次。
那有人就說了,這不 so tm easy 嗎,我遍歷一遍陣列 a[i]
,之後再遍歷一遍陣列 a[j]
,只要 a[i] + a[j] === target
不就 ojbk 了嘛。
那這位同學,我就想問下:你還記得在大學時期,青澀的你,在陽光明媚的演算法與資料結構課堂上,老師的淳淳教誨嗎?還是你把時間都花費在隔壁班的女同學上了?
當然,大家都是上過大學的文化人,我就算這麼做過但也不會這麼說呀!好啦好啦,回到正題,上面的解法時間複雜度是 O(n^2)
因為在每一遍 n 的迴圈中你又跑了一遍 n 的迴圈,所以這是不優雅的。
其實你第二遍迴圈裡只是為了找出一個值,但你用了 O(n)
的複雜度,那這裡能不能更快些呢?當然是可以的啦,我們可以使用二分查詢,只需要 O(logn)
的複雜度,但二分的前提是陣列要有序呀,所以得排個序,排序平均是 O(nlogn)
所以最後我們的時間複雜度就變成了 O(nlogn)
那接下來的步驟就顯而易見了,我貼下程式碼吧:
/**
* 二分查值
*
* @param {Array} arr the data
* @param {nubmer} left the start index
* @param {number} right the end index
* @param {number} val the target value
* @returns {nubmer} the target value's index, if
* no exist, it will return -1
*/
function binarySearch(arr, left, right, val) {
if (right < left) {
throw new Error('right can not less than left')
}
let mid
while (left <= right) {
mid = Math.floor((left + right) / 2)
if (arr[mid] < val) left = mid + 1
else if (arr[mid] === val) break
else right = mid - 1
}
return arr[mid] === val ? mid : -1
}
var twoSum = function(nums, target) {
nums = nums.map((val, idx) => ({
val,
idx
}))
nums.sort(function(a, b) {
return a.val - b.val
})
const len = nums.length
for (let i = 0; i < len - 1; i++) {
const t = target - nums[i].val
const idx = binarySearch(nums.map(v => v.val), i + 1, len - 1, t)
if (idx !== -1) {
return [nums[i].idx, nums[idx].idx]
}
}
}
複製程式碼
這裡提交題目的方式其實就是給你一個 twoSum
函式,你去完成這個函式的功能即可,最後結果如下:
看沒看到,時間上超過了一半的人唉,果然我就是膩害!
那又有人說了:你空間複雜度才超過 5% 的人唉!
就你話多,我有錢,我是富二代,我買幾百個記憶體條給我伺服器裝上,不行啊。
我心裡是這麼想的,當然現實不能真的這樣啦!
那怎麼優化空間複雜度呢?其實 solution 中也講到了,java 是用的 hashMap,我們js也有 Map 呀,所以,你可以用 Map 試試,看看結果能不能超過這麼帥氣、優秀的我!