老姜带你刷力扣系列-0001
老姜带你刷力扣系列-0001
原题:
两数之和(难度:简单)
给定一个整数数组 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
只会存在一个有效答案
进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?
分析:
第一题的难度还是相对比较简单的,从nums数组中找到相加等于target的两个数。 题目阅读理解并不复杂,首先来个暴力解法,双重for循环。
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { // 准备返回结果的vector容器 vector<int> res; for (int i=0;i<nums.size()-1;i++) { // 这里从i+1开始,限定n>=2,不会出错 for (int j=i+1;j<nums.size();j++) { if (nums[i]+nums[j] == target) { // 找到结果直接放入数据return res.push_back(i); res.push_back(j); return res; } } } // 这里不return编译会报错,根据题目限定一定有答案,不会执行到这里 // 实际工程中应该处理这种情况,报错或者返回特定的值给上游处理 return res; } };
提交看结果
执行用时: 416 ms , 在所有 C++ 提交中击败了 8.60% 的用户 内存消耗: 9.7 MB , 在所有 C++ 提交中击败了 96.95% 的用户
是不是很简单,就这?
当然没有这么简单,用时击败了不到10%的用户,仔细看题目的最后有个提示,可不可以有时间复杂度小于 O(n2) 的算法。
老姜出手必须有!
时间小于n2的,按照算法常识,可能的是n或者n*logn
我们先看nlogn,logn的算法一般就是二分查找了,如果需要二分查找,那么就必须有序, 排个序先,因为返回的数据是原始数据的下标,所以排序的同时,需要记录原始的下标,这就有点复杂, 理论上是可以实现的,但是内存占用应该会变大,我这里先不实现,大家可是自己尝试。
另外的可能性就是时间复杂度为n,也就意味着只遍历一遍或者有限次数。 比如示例2中,考虑如何在遍历到4的时候知道已经存在2,而且知道2的下标。 因为遍历的时间复杂度已经是O(n),这里到4找2的过程就必须是O(1)的。
这里当然不得不提HashMap,HashMap的查找时间为O(1),和待查找的数组的长度无关, 因为target是给定的,我们把2的角标存储到HashMap中,那么就可以实现O(1)的查找, 整体遍历是O(n),所以整体算法的时间复杂度是O(n),话不多说上代码!
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { std::map<int,int> index; vector<int> ret; for (int i=0;i<nums.size();i++) { int res = target - nums[i]; auto it = index.find(res); if (it != index.end()) { ret.push_back(it->second); ret.push_back(i); return ret; } else { index.insert(make_pair(nums[i], i)); } } return ret; } };
提交看结果
执行用时:8 ms, 在所有 C++ 提交中击败了 92.34% 的用户 内存消耗:10.7 MB, 在所有 C++ 提交中击败了 20.01% 的用户
时间提上去了,空间占用排位较低,这是算法里面典型的时间换空间,通过构造一个map来提升检索的速度。
你学废了吗?🐶