就个人而言,我是喜欢算法的,奈何不擅长,所以选择了我同样喜欢的前端(我才不会告诉你是因为有好多漂亮妹子在呢)。
那么今天来看下,两数之和:
给你一个数组,返回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 试试,看看结果能不能超过这么帅气、优秀的我!