学算法的前端 leetcode 1

840 阅读3分钟

就个人而言,我是喜欢算法的,奈何不擅长,所以选择了我同样喜欢的前端(我才不会告诉你是因为有好多漂亮妹子在呢)。

那么今天来看下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 试试,看看结果能不能超过这么帅气、优秀的我!