题目:有一个整数数组nums,能否从中取出两个数,使他们的和为target。
输入1:
nums = {4,5,7,10}
target = 12
输出1:
true
输入2:
nums = {4,5,7,10}
target = 8
输出2:
false
题目分析:
首先由给出的输入输出案例,可知取出的两个数不能为同一个数(不放回取数);其次整数数组nums是否有序是不确定的;最后数组中可能会有相同元素,如若数组nums改为{4,4,7,10},输出即为true。
思路分析1:
首先想到的是暴力法:利用双指针,其中一个指针固定,另一个指针遍历数组。时间复杂度O(n*n)。
优化:先将数组进行排序sort,从头遍历数组,假设此时遍历值为value,再使用二分查找剩余数组是否存在 (target-value)。时间复杂度O(n*logn)。
代码实现:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
bool BeSum(vector<int>& nums, int target)
{
sort(nums.begin(), nums.end()); //对数组排序
for (int i = 0; i < nums.size(); ++i) {
if (nums[i] > target) //如果当前元素大于target,后面元素也必定大于target
break;
bool b = binary_search(nums.begin() + i + 1, nums.end(), target-nums[i]); //对后面元素二分查找
if (b)
return true;
}
return false;
}
int main()
{
vector<int> nums;
int n,t;
cin >> n; //输入数组元素个数
for(int i = 0; i < n; ++i)
{
cin >> t;
nums.push_back(t);
}
int target;
cin >> target;
if (BeSum(nums, target))
cout << "true" << endl;
else
cout << "false" << endl;
return 0;
}
思路分析2:
若要得到O(n)的时间复杂度,该如何做呢?若使用时间复杂度为O(1)的哈希查找算法进行查找,便可使整体复杂度降为O(n)。
(1)首先可想到:将数组全部存入哈希表,再进行扫描和查找,但若nums={4,5,7,10},target=8,输出为true,结果错误,pass;
(2)若给每个哈希表项加上一个计数值ct,用来存放数值重复次数,较麻烦,不采用;
(3)更简单的实现:边扫描边构建哈希表,逆向考察思路分析1,从最后一个元素开始扫描,并查找该元素之后的元素是否存在target-value,若查找成功,即返回true,否则将该value加入哈希表,再扫描前一个元素。由对称性可知,从第一个元素开始扫描也具有同样的效果。
代码实现:
#include<iostream>
#include<vector>
#include<unordered_set>
using namespace std;
bool BeSum(vector<int>& nums, int target)
{
unordered_set<int> appeared; //构建哈希表(STL中unorder_set使用哈希实现,set使用红黑树实现)
for (int i = 0; i < nums.size(); ++i) {
if (appeared.count(target - nums[i]))
return true;
appeared.insert(nums[i]);
}
return false;
}
int main()
{
vector<int> nums;
int n, t;
cin >> n; //输入数组元素个数
for (int i = 0; i < n; ++i)
{
cin >> t;
nums.push_back(t);
}
int target;
cin >> target;
if (BeSum(nums, target))
cout << "true" << endl;
else
cout << "false" << endl;
return 0;
}
总结:
对于array的题目,可以优先考虑双针模型。
箴言录
见贤思齐焉,见不贤而内自省也。