「LeetCode」1.两数之和

1,377 阅读3分钟

题目描述🌍

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]

示例 2

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3

输入:nums = [3,3], target = 6
输出:[0,1]

提示

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

暴力枚举🚫

解题思路

枚举数组中的每两个数 xy,寻找数组中是否存在两个数满足 x + y == target

虽然暴力枚举并不优雅,但却简洁一点。

代码

Java

class Solution {
    public int[] twoSum(int[] nums, int target) {
        for (int i = 0; i < nums.length - 1; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[i] + nums[j] == target)
                    return new int[]{i, j};
            }
        }
        return null;
    }
}

C++

class Solution {
public:
    vector<int> twoSum(vector<int> &nums, int target) {
        int length = nums.size();
        for (int i = 0; i < length - 1; ++i) {
            for (int j = i + 1; j < length; ++j) {
                if (nums[i] + nums[j] == target) {
                    return {i, j};
                }
            }
        }
        return {};
    }
};

C

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int *twoSum(int *nums, int numsSize, int target, int *returnSize) {
    for (int i = 0; i < numsSize - 1; ++i) {
        for (int j = i + 1; j < numsSize; ++j) {
            if (nums[i] + nums[j] == target) {
                int *ret = (int *) malloc(2 * sizeof(int));
                ret[0] = i;
                ret[1] = j;
                *returnSize = 2;
                return ret;
            }
        }
    }
    *returnSize = 0;
    return NULL;
}

Python3

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        lens = len(nums)
        for i in range(0, lens - 1):
            for j in range(i + 1, lens):
                if target == nums[i] + nums[j]:
                    return [i, j]
        return []

Go

func twoSum(nums []int, target int) []int {
    for i := 0; i < len(nums)-1; i++ {
        for j := i + 1; j < len(nums); j++ {
            if nums[i]+nums[j] == target {
                // var ret []int = []int{i, j}
                // ret := []int{i, j}
                // return ret
                return []int{i, j}
            }
        }
    }
    return []int{}
}

JavaScript

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

时间复杂度:O(n2)O(n^2)

空间复杂度:O(1)O(1)

哈希表🚀

解题思路

注意到暴力枚举的时间复杂度较高的原因是双重循环遍历的时间复杂度过高。因此,我们需要一种更优秀的方法,能够快速寻找数组中是否存在目标元素。如果存在,我们需要找出它的索引。

如果你此前没有遇到过哈希表这类方法,从现在起就将其记住,因为它会作为一种常见的解法出现在各类「数组」标签的题解中。

首次引入「哈希表」:对于数组遍历过程中的当前值 x,我们首先查询哈希表中是否存在 (前面插入值的所需值) x,然后将 (供后面插入元素对比的值) target - x 插入到哈希表中;一旦当前值 x 在哈希表中存在,则表示哈希表中存在某个值与当前值 x 相加等于 target.

代码

Java

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();

        for (int i = 0; i < nums.length; i++) {
            if (map.containsKey(nums[i])) {
                return new int[]{map.get(nums[i]), i};
            }
            map.put(target - nums[i], i);
        }

        return null;
    }
}

C++

class Solution {
public:
    vector<int> twoSum(vector<int> &nums, int target) {
        unordered_map<int, int> hashtable;
        for (int i = 0; i < nums.size(); ++i) {
            auto it = hashtable.find(nums[i]);
            // find(): 未找到则返回 hashtable.end()
            if (it != hashtable.end()) {
                return {i, it->second};
            }
            hashtable[target - nums[i]] = i;
        }
        return {};
    };
};

Python3

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        hashtable = dict()
        # Python 中使用 enumerate 可同时获得「索引」与「值」
        for i, num in enumerate(nums):
            if num in hashtable:
                return [hashtable[nums[i]], i]
            hashtable[target - nums[i]] = i
        return []

Go

func twoSum(nums []int, target int) []int {
    dict := map[int]int{}
    for index, num := range nums {
        // if表达式的特殊写法
        if value, ok := dict[num]; ok {
            return []int{value, index}
        }
        dict[target-num] = index
    }
    return nil
}

JavaScript

var twoSum = function (nums, target) {
  let hashMap = new Map()
  for (let i = 0; i < nums.length; i++) {
    if (hashMap.has(nums[i])) {
      // get()
      return [i, hashMap.get(nums[i])]
    }
    // set()
    hashMap.set(target - nums[i], i)
  }
  return []
};

时间复杂度:O(n)O(n)

空间复杂度:O(1)O(1)

芝士点🛕

「Java」HashMap 之 put()

put(K key, V value)

「C++」unordered_mapmap

  • map 底层是用红黑树实现,最大的特点就是有序,而且许多操作(如插入、删除等)均可在 O(logn)O(logn) 内完成;缺点就是空间占用率高,每一个节点都保留了许多额外的信息。
  • unordered_map 底层则是用哈希表实现,查找操作十分高效;但哈希表的建立比较耗费时间。

「Python」enumerate

Python 提供的内置函数 numerate() 可以对 for 循环遍历进行包装(仅对可遍历的数据对象生效),可同时获取「索引」与「值」:

# enumerate
languages = ['C++', 'Java', 'Go', 'Python']
print(list(enumerate(languages)))  # [(0, 'C++'), (1, 'Java'), (2, 'Go'), (3, 'Python')]
print(list(enumerate(languages, 1)))  # [(1, 'C++'), (2, 'Java'), (3, 'Go'), (4, 'Python')]

'''
C++
Java
Go
Python
'''
# 普通 for 循环
for lang in languages:
    print(lang)

'''
0 C++
1 Java
2 Go
3 Python
'''
for index, value in enumerate(languages):
    print(index, value)

最后🌅

该篇文章为 「LeetCode」 系列的 No.1 篇,在这个系列文章中:

  • 尽量给出多种解题思路
  • 提供题解的多语言代码实现
  • 记录该题涉及的知识点

👨‍💻争取做到 LeetCode 每日 1 题,所有的题解/代码均收录于 「LeetCode」 仓库,欢迎随时加入我们的刷题小分队!