【leetcode】1. two sum两数之和

leetcode(1): two sum

LeetCode 第一题,难度 Easy,但很适合拿来练手:题意直白,优化路径也清晰。

题意理解

给定一个整数数组 nums 和一个目标值 target,在数组里找出两个不同下标 ij,使得 nums[i] + nums[j] === target。题目保证恰好有一组解,且不能重复使用同一个元素。

Example 里 nums = [2, 7, 11, 15]target = 9,答案是 [0, 1],因为 2 + 7 = 9。注意返回的是下标,不是数值本身。

Description

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

Example:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

0. My solution(Brute Force)

最直觉的做法:双重循环,枚举所有 (i, j) 组合,看两数之和是否等于 target。内层从 0 开始扫,所以 i === j 时要用 i != j 挡一下。

var twoSum = function(nums, target) {
    for(let i = 0; i < nums.length; i++) {
        for(let j = 0; j < nums.length; j++) {
            if(nums[i] + nums[j] === target && i != j) {
                return [i, j]
            }
        }
    }
};

· Time complexity: O(n^2), For each element, I try to find its complement by looping through the rest of array which takes O(n)*O(n) time. Therefore, the time complexity is O(n^2).

· Space complexity : O(1).

暴力法能过,但数据量一大就超时。核心问题是:对每个 nums[i],我们都在数组里线性找它的「补数」target - nums[i]

1. Improve

内层循环从 i + 1 开始,避免 (i, j)(j, i) 重复判断,也省掉 i != j 的判断。

var twoSum = function(nums, target) {
    for(let i = 0; i < nums.length; i++) {
        for(let j = i + 1; j < nums.length; j++) {
            if(nums[i] + nums[j] === target) {
                return [i, j]
            }
        }
    }
};

时间复杂度仍是 O(n²),只是常数小一点。

2. Improve again

indexOfi 之后找补数,写法更短。indexOf(complement, i + 1) 保证找到的下标在 i 后面,不会用到同一个元素。

var twoSum = function(nums, target) {
    for(var i = 0; i< nums.length; i++){
        var complement = target - nums[i];
        var found = nums.indexOf(complement, i + 1);
        if(found !== -1){
            return [i, found];
        }
    }
    return [0, 0];
};

indexOf 本身也是 O(n),整体还是 O(n²)。另外最后一行 return [0, 0] 按题意其实走不到(保证有解),留着算是防御性写法。

3. Improve again

哈希表(JavaScript 里用普通对象 {} 就行):先一遍把「值 → 下标」存进去,再一遍查补数是否在表里。查表 O(1),总时间 O(n)。

var twoSum = function(nums, target) {
    if (nums.length === 2) return [0, 1];
    const len = nums.length;
    let hashTable = {};
	for(let i = 0; i < len; i++){
		// Add a new obj to the hashTable where key = nums[i] and value = i
		hashTable[nums[i]] = i;
	}

    for(let i = 0; i < len; i++) {
        let complement = target - nums[i];
        let found = hashTable[complement]; // Determine whether the complement exist in the hashTable
        if(found !== undefined && found != i) return [i, found];
	}
	return [0,0];
}

JavaScript 实现注意点

  • 对象键会被转成字符串,本题全是整数,一般没问题;若值可能是对象或 NaN,要另想方案。
  • found != i 用宽松不等即可,下标都是数字。
  • 也可以边遍历边写入哈希表,只扫一遍;上面写法是「先建表再查」,逻辑更直观。
  • 长度只有 2 时直接返回 [0, 1],小优化,可写可不写。

B站视频地址

版权声明: 本文首发于 指尖魔法屋-【leetcode】1. two sum两数之和https://blog.thinkmoon.cn/post/295-notes-leetcode-two-sum/) 转载或引用必须申明原指尖魔法屋来源及源地址!