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)的算法慢。
版权声明:本文为博主原创文章,未经博主允许不得转载。