两数之和等于目标值
1. LeetCode(twoCode)
Given an array of integers, find two numbers such that they add upto a specific target number.
The function twoSum should return indices of the two numbers suchthat they add up to the target, where index1 must be less than index2. Pleasenote 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
2. 问题分析
(1) 暴力求解,检查全部对(n2)数字之和。
(2) 题目中并没有说数组是有序或无序,因此能够先按有序思考,毕竟排序方法太成熟了。若数组元素有序则能够从数组的两端開始求解,若两个数之和大于目标值则后端往前移,若小于则前端往后移。若相等则找到结果程序结束。
若前后端相遇则查找失败。显然该方法时间复杂度O(n)。
(3) 若无序,则有两种处理思路:
(3.1) 先对数组进行排序,最快的排序算法O(n),则无序处理代价为O(n) + O(n) = O(n)。
进一步分析:经常使用的线性时间排序算法计数排序、桶排序等均有一定的限制。如当数组中元素跨度较大时计数排序须要较大的空间开销,桶排序要求元素均匀分布。
因此对数组排序常使用快排(O(nlogn)),此时问题求解时间复杂度O(nlogn)。
(3.2) 用哈希存储原始数据。
遍历原始数据,当訪问数据是w,目标值是t时,在哈希表中查找是否存在t-w,若存在则查找成功。若不存在,则继续遍历。直到找到结果或遍历结束。
哈希插入与查找代价均为O(1),因此总体时间复杂度仍是O(n)。
3 实现
3.1 暴力方法
该方法检測n2对元素的和,时间复杂度O(n2)。
vector<int>Solution::twoSum(vector<int> &nums, const int target) { vector<int> result; int size = nums.size(); for (int i = 0; i < size; ++i) { for (int j = i + 1; j < size;++j) { if (target == nums[i] +nums[j]) { result.push_back(i + 1); result.push_back(j + 1); break; } } } return result; }
3.2 先排序再求解1
在这里利用了C++STL中的sort排序函数。从sort源代码实现来看其属于快排的优化,具体的请查看sort源代码。该方法时间复杂度O(nlogn)。
struct numStruct { int number; int location; numStruct(int a = 0, int b = 0):number(a),location(b) { } }; boolisLesser(numStruct a, numStruct b) { return a.number < b.number; } vector<int>Solution::twoSum(vector<int> &nums, const int target) { vector<int> result; int size = nums.size(); numStruct *data = new numStruct[size]; for (int i = 0; i < size; ++i) { data[i].number = nums[i]; data[i].location = i; } sort(data, data + size, isLesser); int head, tail; head = 0; tail = size - 1; while (head < tail) { int tempResult = data[head].number+ data[tail].number; if (target == tempResult) { int loc1, loc2; loc1 = data[head].location +1; loc2 = data[tail].location +1; if (loc1 > loc2) { result.push_back(loc2); result.push_back(loc1); } else { result.push_back(loc1); result.push_back(loc2); } break; } else if (target < tempResult) { --tail; } else { ++head; } } delete []data; return result; }
3.3先排序再求解2
(1) 非常多博客里都把使用map当做哈希的方法,实际是错误的。由于map的底层是红黑树而不是哈希,红黑树中序有序。
该方法时间复杂度O(nlogn)。
vector<int>Solution::twoSum(vector<int> &nums, const int target) { vector<int> result; map<int, int> data; int size = nums.size(); for (int i = 0; i < size; ++i) { if (!data.count(nums[i])) { data.insert(pair<int,int>(nums[i], i)); } } for (int i = 0; i < size; ++i) { if (data.find(target - nums[i]) !=data.end()) { result.push_back(data[target- nums[i]] + 1); result.push_back(i + 1); break; } } return result; }
(2) 由于仅仅须要找到一个结果。因此不是必需索引所有的数据。上述代码优化为:
vector<int>Solution::twoSum(vector<int> &nums, const int target) { vector<int> result; map<int, int> data; int size = nums.size(); for (int i = 0; i < size; ++i) { if (data.find(target - nums[i]) !=data.end()) { result.push_back(data[target- nums[i]] + 1); result.push_back(i + 1); break; } data[nums[i]] = i; } return result; }
(3) 又由于红黑树中序有序,因此能够利用中序遍历。
中序遍历为:左-中-右。而逆向中序遍历为右-中-左。值得注意的是map中的键值唯一。因此使用multimap来取代map。
综上。能够像3.2中那样实现。
vector<int>Solution::twoSum_Map(vector<int> &nums, const int target) { vector<int> result; multimap<int, int> data; int size = nums.size(); for (int i = 0; i < size; ++i) { data.insert(pair<int,int>(nums[i], i)); } multimap<int, int>::iterator it =data.begin(); multimap<int, int>::reverse_iteratorrit = data.rbegin(); int items = 0; size = data.size(); while (items++ < size) { int tempResult = (*it).first +(*rit).first; if (target == tempResult) { int loc1, loc2; loc1 = (*it).second + 1; loc2 = (*rit).second + 1; if (loc1 > loc2) { result.push_back(loc2); result.push_back(loc1); } else { result.push_back(loc1); result.push_back(loc2); } break; } else if (target < tempResult) { ++rit; } else { ++it; } } return result; }
3.4 哈希实现方式
unordered_map: 该函数即哈希函数。该方法时间复杂度O(n)。
vector<int>Solution::twoSum1(vector<int> &nums, const int target) { vector<int> result; unordered_map<int, int> data; int size = nums.size(); for (int i = 0; i < size; ++i) { if (data.find(target - nums[i]) !=data.end()) { result.push_back(data[target- nums[i]] + 1); result.push_back(i + 1); break; } data[nums[i]] = i; } return result; }
4. 效率
在LeetCode系统中。上述代码效率不同。另外须要注意的是尽管使用哈希时间复杂度较低,可是每次插入结点时须要申请空间。因此当n较小时哈希反而比先排序的时间复杂度为O(nlogn)的算法慢。