本文原由:
《学习剑指offer——数据结构与算法名企面试题精讲》中:
面试题61:和最小的k个数对
题目:给定两个递增排序的整数数组,从两个数组中各取一个数字u和v组成一个数对(u,v),请找出和最小的k个数对。例如,输入两个数组[1,5,13,21]和[2,4,9,15],和最小的3个数对为(1,2)、(1,4)和(2,5)。
讲解最小堆解法时,用到了lambda表达式,但是C++语法稍有区别,所以重新学习了《C++ Primer》中lambda和关联容器的相关内容。
一个lambda表达式具有如下形式(《C++ Primer》P346):
[capture list] (parameter list) -> return type { function body }
指定lambda返回类型(P353):
默认情况下,如果一个lambda体包含return之外的任何语句,则编译器假定此lambda返回void。与其他返回void的函数类似,被推断返回void的lambda不能返回值。
关联容器对关键字类型的要求(《C++ Primer》P378):
对于有序容器——map、multimap、set以及multiset,关键字类型必须定义元素比较的方法。
传递给排序算法的可调用对象必须满足与关联容器中关键字一样的类型要求。
所提供的操作必须在关键字类型上定义一个严格弱序。
面试题61:和最小的k个数对
①使用最小堆C++解法代码:
vector<pair<int, int>> kSmallestPairs(const vector<int>& nums1, const vector<int>& nums2, int k)
{
auto comp = [&nums1, &nums2](const pair<int, int>& left, const pair<int, int>& right)
{return nums1.at(left.first) + nums2.at(left.second) > nums1.at(right.first) + nums2.at(right.second); };
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(comp)> q(comp);
vector<pair<int, int>> result;
for (int i = 0; i < k && i < nums1.size(); ++i)
{
q.push({ i, 0 });
}
while (k-- > 0 && !q.empty())
{
pair<int, int> top = q.top();
result.push_back({ nums1.at(top.first), nums2.at(top.second) });
q.pop();
if (top.second < nums2.size() - 1)
q.push({ top.first, top.second + 1 });
}
return result;
}
lambda的局限(《C++标准库 第2版》P232):
由于priority_queue声明式需要指明lambda类型,所以必须使用decltype,它会为一个lambda对象产出类型。注意,你也必须把lambda对象传入给q的构造函数,否则q会调用被传入的排序准则的default构造函数,而根据C++语言规则,lambda没有default构造函数,也没有assignment操作符。基于这些局限,"以class定义某个函数对象作为排序准则"说不定还比较直观些。
lambda的另一个问题是,它们无法拥有"跨越多次调用"都能被保存下来的内部状态(internal state)。如果你需要这样的状态,必须在外围作用域中声明一个对象或变量,将它(搭配某种capture)以by_reference方式传入lambda。与此相比,函数对象允许你封装内部状态。
②使用最大堆C++解法代码:
//排序准则
class comparePairSumLess
{
public:
bool operator()(const pair<int, int>& left, const pair<int, int>& right)
{
return left.first + left.second < right.first + right.second;
}
};
//9_61和最小的k个数对
vector<pair<int, int>> kSmallestPairs(const set<int>& nums1, const set<int>& nums2, int k)
{
priority_queue <pair<int, int>, vector <pair<int, int>>, comparePairSumLess> q;
vector<pair<int, int>> result;
auto cur1 = nums1.begin();
for (int i = 0; i < k && i < nums1.size(); ++i, ++cur1)
{
auto cur2 = nums2.begin();
for (int j = 0; j < k && j < nums2.size(); ++j, ++cur2)
{
int sum = *cur1 + *cur2;
q.push({ *cur1,*cur2 });
if (q.size() > k)
{
q.pop();
}
}
}
while (!q.empty())
{
result.push_back(q.top());
q.pop();
}
return result;
}