趁着学期结束,开始慢慢的刷LeetCode里面的题了,不求速度,希望能够熟练的掌握解题思路,并提高编程能力。
LeetCode题库网址:https://leetcode.com/problemset/algorithms/
今天就从第一题开始刷,以后可能不按顺序做。
Given an array of integers, find two numbers such that they add up to a specific target number.
The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.
You may assume that each input would have exactly one solution.
Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2
题目的大概意思是,给你一个数组numbers和一个数值target,在数组中找到两个加起来等于target的元素的位置。注意:位置下标从1开始的。
看到题的第一反应就是两层for循环,一旦找到满足条件的两个元素位置,就return。这是一种暴力解法,于是就敲了下面的代码:
class Solution{ public: vector<int> twoSum(vector<int>& data, int target) { vector<int> index; for (int i = 0; i < data.size(); ++i){ for (int j = i + 1; j < data.size(); ++j){ if (data.at(i) + data.at(j) == target){ index.push_back(i + 1); index.push_back(j + 1); return index; } } } return index; } };提交代码后,发现该题并没有被AC掉,而给出了算法超时信息;既然是有时间限制的,于是只有想其他办法了。。。
①朴素法(暴力法),时间复杂度为o(n^2)
②双指针法,时间复杂度为o(n*logn)
③hash表法,时间复杂度为o(n)
我的解法属于方法①,在上述办法中,只有方法②③才能AC掉。
思路是:将原始数组data拷贝,然后排序,结果记为cdata。设两个下标i,j分别指向cdata的第一个元素和最后一个元素位置:计算result=cdata[i]+cdata[j];如果resut>target,说明两元素之和大了,需要将j的下标向前移动一位(cdata为有序,cdata[--j]会变小);如果result<target,说明两元素之和小了,需要将i的下标向后移动一位(cdata为有序,cdata[++i]会变大),如此反复计算result,并移动下标,直到result==target为止。
class Solution { public: vector<int> twoSum(vector<int>& data, int target) { vector<int>cdata(data);//用于存储排序数据 vector<int>index;//索引 partial_sort_copy(data.begin(), data.end(), cdata.begin(), cdata.end());//拷贝排序 int i = 0, j = cdata.size() - 1;//双下标 int result = cdata.at(i) + cdata.at(j); while(result != target){ if (result > target){ --j;//向前移动下标 } else{ ++i;//向后移动下标 } result = cdata.at(i) + cdata.at(j); } int pos = find(data.begin(), data.end(), cdata.at(i)) - data.begin(); index.push_back(pos + 1); data.at(pos) = cdata.at(j) + 1;//防止该位置被二次检索 index.push_back(find(data.begin(), data.end(), cdata.at(j)) - data.begin() + 1); sort(index.begin(), index.end()); return index; } };提交代码后,顺利AC掉。 run time 为20ms。
用hash表来做此题,在STL中有map容器可以做,但是目前还没有学习这个容器,等学习完了再来更新此解法。在这里依然是用的vector容器,该没有map里面的一些方法,所以操作起来要复杂些。
思路是:一层循环体,下标为i。在hash_table中查找data[i],如果没有则假如hash_table中;如果有,则记录该位置pos1=i。在hash_table中查找target-data[i],如果有没有则丢弃pos1,开始下一个循环(即++i);如果有,则记该位置为pos2。最终pos1,pos2都找到时,算法结束。
class Solution { public: vector<int> twoSum(vector<int>& data, int target) { vector<int> hash_table; for (int i = 0; i < data.size(); ++i){ hash_table.push_back(data.at(i)); int pos = find(hash_table.begin(), hash_table.end(), target - data.at(i)) - hash_table.begin(); if (pos < hash_table.size() && i != pos){//found index.push_back(pos + 1); index.push_back(i + 1); return index; } } return index; } };提交代码后,顺利AC掉。 run time 为308ms。
更新(2016-01-09):下面用map做哈希表,代码如下:
class Solution { public: vector<int> twoSum(vector<int>& data, int target) { map<int, int> hash_table; vector<int> index; for(int i = 0; i < data.size(); ++i){ if (!hash_table.count(data.at(i))){//not found hash_table.insert(make_pair(data.at(i), i));//key为值,value为下标 } if (hash_table.count(target - data.at(i))){ int n = hash_table.at(target - data.at(i)); if (n != i){ index.push_back(n + 1); index.push_back(i + 1); break; } } } sort(index.begin(), index.end()); return index; } };提交代码后,顺利 AC 掉。 run time 为28ms。