1.两数之和
一、问题描述
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
二、问题简化
这个问题可以简化为:
“在一个数组中,寻找两个元素,可通过一个计算得到目标值”。
经过研究集合的定义,这里还是应该用数组描述比较恰当。数组是有序的、可重复的;多重集是无序的、可重复的;而集合是无序的、不可重复的。
元素可以不用是整数,用T表示。
计算不一定是求和,用fn表示。
答案可能不止一种,所以我们直接返回所有答案,而答案是一个二元组,所以最终返回的是二元组的数组,即:
vector<tuple<T, T>>
类型T的集合用代码表示为:
vector<T>
所以整个函数可以表示为:
template<typename T, typename F>
vector<tuple<T, T>> FindTwoToOne(const T& target, const vector<T>& vec, F fn);
三、功能实现
我通过遍历所有元素,当前元素与其后的所有元素做一次运算再比较目标值即可。这样完成了“每个元素与其他元素只且计算一次”。
template<typename T, typename F>
vector<tuple<T, T>> FindTwoToOne(const T& target, const vector<T>& vec, F fn)
{
vector<tuple<T, T>> ret;
//数组大小
int size = vec.size();
//小于2个元素,返回空结果
if (size < 2)
return ret;
//遍历 (不包含最后一个元素)
for (int i = 0; i != size - 1; ++i)
{
//一直计算之后的所有元素
for (int j = i + 1; j != size; ++j)
{
if (fn(vec[i], vec[j]) == target)
ret.push_back({ i, j });
}
}
return ret;
}
四、测试结果
我用到了元组、模板、lambda表达式、auto,绝对是现代的C++写法。而网上的一般写法使用了哈希表,并且使用了加法计算的逆运算是减法这一特性,我的写法虽然没有降低复杂度,但更加普遍适用。
//1.两数之和
#include <iostream>
#include <vector>
#include <tuple>
using namespace std;
template<typename T, typename F>
vector<tuple<T, T>> FindTwoToOne(const T& target, const vector<T>& vec, F fn)
{
vector<tuple<T, T>> ret;
//数组大小
int size = vec.size();
//小于2个元素,返回空结果
if (size < 2)
return ret;
//遍历 (不包含最后一个元素)
for (int i = 0; i != size - 1; ++i)
{
//一直计算之后的所有元素
for (int j = i + 1; j != size; ++j)
{
if (fn(vec[i], vec[j]) == target)
ret.push_back({ i, j });
}
}
return ret;
}
int main()
{
//目标值
int target = 10;
//源数组
vector<int> vec = { 1,5,3,2,18,7,5,4,6,3,0,4,9,7,5,8,8,6,4,2 };
//执行函数
auto ret = FindTwoToOne(target, vec, [](int a, int b)
{
return a + b;
});
//输出结果
for (auto& iter : ret)
{
cout << "[" << get<0>(iter) << ", " << get<1>(iter) << "]"
<< " -> " << vec[get<0>(iter)] << " and " << vec[get<1>(iter)] << endl;
}
return 0;
}