解法一:暴力解法
暴力解法的思路较为直接,在输入数组中分别对两个下标进行遍历,若满足题目要求,直接返回结果(题目中明确说明假设答案唯一)。
注意:
- 第一层循环应从0到n-2位置(n为数组长度),即不需要遍历到数组最后一个元素;
- 第二层循环应将第一层循环变量作为起点,遍历至数组最后一个元素。
- 下标从1开始,因此在保存结果时,要注意加1。
代码如下:
class Solution { public: /** * * @param numbers int整型vector * @param target int整型 * @return int整型vector */ vector<int> twoSum(vector<int>& numbers, int target) { int n = numbers.size(); vector<int> res; // 数组为空 if (!n) return res; // 两层循环 暴力求解 for (int i = 0; i < n - 1; i ++) { // 从遍历至n-2位置 for (int j = i + 1; j < n; j ++) { // 从i遍历至n-1位置 if (numbers[i] + numbers[j] == target) { // 下标从1开始 res.push_back(i + 1); res.push_back(j + 1); return res; } } } return res; } };
该方法采用两层循环遍历原数组,因此时间复杂度为平方级别,即O(N^2);该方法未定义额外空间(除保存结果所需要的数组外),因此空间复杂度为O(1)。
解法二:哈希表
上述方法的时间复杂度可以进一步优化至O(N)。
哈希表的思想为「以空间换时间」,这是由于哈希表保存了键值对,其「查找」复杂度为O(1)。
对于本题,具体解题思路为(如图所示):
定义哈希表hashmap,其存放的键值对为<取值,下标>。
从开始处遍历数组,对于第i个位置,在哈希表中寻找target-nums[i]是否存在,若存在,将两个下标放入数组中返回;若不存在,将其添加至表中,继续遍历。
注:在将结果保存至数组中时,无需判断二者下标大小:当所遍历到的位置符合题目要求时,说明哈希表中已经存在待查找的另一元素,因此「已存在于表中的元素的下标」一定小于「当前位置元素的下标」。
代码实现如下:
class Solution { public: /** * * @param numbers int整型vector * @param target int整型 * @return int整型vector */ vector<int> twoSum(vector<int>& numbers, int target) { int n = numbers.size(); vector <int> res; if (!n) return res; // 定义哈希表,unordered_map是用哈希表实现的,复杂度为O(1),而map是用红黑树实现的 unordered_map<int, int> hashmap; for (int i = 0; i < n; i ++) { if (hashmap.find(target - numbers[i]) != hashmap.end()) { // find函数返回hashmap.end()代表未找到,否则代表找到 // 将结果存入数组 res.push_back(hashmap[target - numbers[i]] + 1); res.push_back(i + 1); break; } else { hashmap[numbers[i]] = i; // 将未找到的值插入哈希表中,继续遍历 } } return res; } };
此解法只遍历数组一次,且在哈希表中查找的时间复杂度为O(1),因此总时间复杂度为O(N);该方法需要构建哈希表,因此空间复杂度为O(N)。