原题 leetcode 628
如果选择3个最大数, 3是硬编码的, 那么其思路是
- 基于排序: 对原数组排序, 然后取最大的3个正数, 最小的2个负数与最大的整数,求积,得到较大者; 由于基于比较的排序上线是O(nlogn)的, 基于排序的时间复杂度是O(nlogn);
- 不排序: 类似于查找最大的k个数, 由于k一般远远小于n,可以认为时间复杂度是O(n)的, 可以利用声明k个元素的数组 + flags数组,来得到最大的3个数以及最小的2个负数; 其他思路一样;
对这道题扩展了一下要求, 若K是任意的? 上面的思路,不适用于K是任意的情况, 因为当K是任意的, 正数和负数的组合可能是穿插的;
隔了一天, 想到了第二个思路:
最大的k个数积: n个数中选任意3个, 求出积最大的那个, 实际上就等效于n个元素的集合选出k=3的子集, 然后遍历该子集求得到积的值最大的子集。代码求子集的方法可以用回溯法, 这个方法的思路和实现都很简洁;
给出下面的一个实现:
#include <iostream>
#include <vector>
#include <climits>
#define K 3
using namespace std;
class Solution {
public:
int maximumProduct(vector<int>& nums) {
robot(0, K, new vector<int>(), nums);
int max = INT_MIN;
for (auto item : output) {
int one_result = 1;
// cout << "one item: ";
for (auto num : item) {
//cout << num << " ";
one_result *= num;
}
//cout << endl;
//cout << "one_result=" << one_result << endl;
max = std::max(max, one_result);
}
//cout << "max=" << max << endl;
return max;
}
private:
void robot(int idx, int k, vector<int>* curr, vector<int>& nums);
vector<vector<int>> output;
};
void Solution::robot(int idx, int k, vector<int>* curr, vector<int>& nums) {
if (static_cast<int>(curr->size()) == k) {
output.push_back(*curr);
return;
}
for (size_t i = idx; i < nums.size(); ++i) {
curr->push_back(nums[i]);
robot(i + 1, k, curr, nums);
curr->pop_back(); //回溯
}
}
这个方法可以解决部分输入样本, 但leetcode提供了一个10000个元素的样本, 出现了TLE;
针对这个问题,想到了一点优化方案:
先对整个集合排序,由于可能会有负数的存在,于是选取以下集合:
- 尾部的k个最大的大于等于0的数;
- 首部选取neg_count个可能的负数;
neg_count是这样的数: 选集合中实际的负数的个数与k,选择2者较小者, 记为l, 找到最大的小于l的2的倍数个的数; - 以找到的k个正数和neg_count个负数的集合为集合,用上述的回溯法求出所有势为K的子集, 找出其中最大的积即可.
这样把,所有回溯法的样本空间的时间复杂度就被约束到了较小的空间, 如果K=3, 则回溯法的n=5;
这个回溯法robot的时间复杂度是很高的,类似N皇后问题, 这个时间复杂度是O(n!)级别的
这个思路的方法实现, 是可以通过的,代码如下:
#include <iostream>
#include <vector>
#include <climits>
#include <set>
#include <algorithm>
#define K 3
using namespace std;
class Solution {
public:
int maximumProduct(vector<int>& nums) {
std::sort(nums.begin(), nums.end());
if(nums.size() == 1)
return nums[0];
if (nums.size() == 3) {
return nums[0] * nums[1] * nums[2];
}
int count = K;
vector<int> pos;
for (auto ed = nums.end() - 1; ed >= nums.begin(); --ed) {
if (*ed >= 0 && count > 0){
pos.push_back(*ed);
--count;
}
}
vector<int> neg = findNegative(nums, K);
pos.insert(pos.end(), neg.begin(), neg.end());
cout << "pos.size()=" << pos.size() << endl;
robot(0, K, new vector<int>(), pos);
int max = INT_MIN;
for (auto item : output) {
int one_result = 1;
// cout << "one item: ";
for (auto num : item) {
// cout << num << " ";
one_result *= num;
}
cout << endl;
// cout << "one_result=" << one_result << endl;
max = std::max(max, one_result);
}
// cout << "max=" << max << endl;
return max;
}
private:
void robot(int idx, int k, vector<int>* curr, vector<int>& nums);
vector<int> findNegative(const vector<int>& unqiue_nums, int k);
vector<vector<int>> output;
};
void Solution::robot(int idx, int k, vector<int>* curr, vector<int>& nums) {
if (static_cast<int>(curr->size()) == k) {
output.push_back(*curr);
return;
}
for (size_t i = idx; i < nums.size(); ++i) {
curr->push_back(nums[i]);
robot(i + 1, k, curr, nums);
curr->pop_back();
}
}
vector<int> Solution::findNegative(const vector<int>& unique_nums, int k) {
int negative_count = 0;
for (auto num : unique_nums)
if (num < 0)
negative_count++;
int count = 1;
int limit = std::min(k, negative_count);
limit = limit % 2 == 0 ? limit : limit - 1;
vector<int> result;
for (int i = 0; i < limit; ++i) {
result.push_back(unique_nums[i]);
}
return result;
}
另外, 参考一个k是3硬编码的实现: