题目
给你一个整型数组 nums ,在数组中找出由三个数组成的最大乘积,并输出这个乘积。
示例 1:
输入:nums = [1,2,3]
输出:6
示例 2:
输入:nums = [1,2,3,4]
输出:24
示例 3:
输入:nums = [-1,-2,-3]
输出:-6
提示:
3 <= nums.length <= 104
-1000 <= nums[i] <= 1000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-product-of-three-numbers
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
常规解题思路O(nlogn)
题目要求获得3个数最大的乘积。
由于:
- 越大的非负数乘积越大,所以尽量取最大的3个非负数
- 正数 > 0 > 负数,负负才能得正,若有负数时应尽量取2个负数
- 负数的数值部分越大,负数越小,仅有时应取数值部分较小的负数(即较大的负数)
所以最大的三个数之积,较可能偏向,2个最小负数之积再乘最大非负数,或三个最大非负数之积
分三种情况讨论:
- 负数2个以下,即输入全为非负数、或有1个负数的情况(负负得正,仅有一个负没法得到正)
直接取3个最大的非负数之积 - 输入全为负数
直接取3个最大的负数之积 - 输入负数与非负数均有
- 非负数大于0个但小于3个,负数含2个及以上(必然会用到最少一个负数,负负才能得正,所以用两个负数)
直接取最小两个负数之积 * 最大非负数 - 非负数含3个及以上,负数2个及以上
取 (最小两个负数之积 * 最大非负数) 与 (三个最大非负数之积) 的较大者
- 非负数大于0个但小于3个,负数含2个及以上(必然会用到最少一个负数,负负才能得正,所以用两个负数)
代码
class Solution {
public:
int maximumProduct(vector<int>& nums) {
if(3 == nums.size()){
return nums[0] * nums[1] * nums[2];
}
std::sort(nums.begin(), nums.end()); //O(nlogn)
auto itZero = std::lower_bound(nums.begin(), nums.end(), 0); //寻找首个非负数 O(nlogn)
int iSize = static_cast<int>(nums.size());
int iNegativeSize = std::distance(nums.begin(), itZero);
int iNonNegativeSize = iSize - iNegativeSize;
if(iNegativeSize < 2){ //全非负或仅有1个负数(需要两个负数积才为正),直接取最大三个非负数的积
return nums[iSize - 3] * nums[iSize - 2] * nums[iSize - 1];
}
else if(0 == iNonNegativeSize){ //全负数,直接取最大3个负数的积
return nums[iNegativeSize - 3] * nums[iNegativeSize - 2] * nums[iNegativeSize - 1];
}
else{ //负数与非负数均有
if(iNonNegativeSize <= 2){ //非负只有1个或2个,取较大非负数乘2个最小负数的积
return nums[iSize - 1] * nums[0] * nums[1];
}
//同时拥有2个及以上负数,3个及以上非负数
int iMaxNegativeProduct = nums[0] * nums[1]; //2个最小负数的积
return iMaxNegativeProduct * nums[iSize - 1] > nums[iSize - 3] * nums[iSize - 2] * nums[iSize - 1] ?
iMaxNegativeProduct * nums[iSize - 1] : nums[iSize - 3] * nums[iSize - 2] * nums[iSize - 1];
}
}
};
代码简化
观察发现:
- if(iNegativeSize < 2) 时 取了 最大三个值的乘积
- else if(0 == iNonNegativeSize) 时 iNegativeSize与iSize相等,也同样取了 最大三个值的乘积
*else{ //负数与非负数均有- if(iNonNegativeSize <= 2)时,取了 最大非负数乘最小两个负数之积
- 剩余情况 取了 (最小两个负数之积 * 最大非负数) 与 (三个最大非负数之积) 的较大者
因此代码可以简化如下:
class Solution {
public:
int maximumProduct(vector<int>& nums) {
if(3 == nums.size()){
return nums[0] * nums[1] * nums[2];
}
std::sort(nums.begin(), nums.end()); //O(nlogn)
int iSize = static_cast<int>(nums.size());
int iMaxNegativeProduct = nums[0] * nums[1]; //2个最小负数的积
int iMaxNonNegativeProduct = nums[iSize - 3] * nums[iSize - 2] * nums[iSize - 1];
return iMaxNegativeProduct * nums[iSize - 1] > iMaxNonNegativeProduct ?
iMaxNegativeProduct * nums[iSize - 1] : iMaxNonNegativeProduct;
}
};
O(n)解题思路
一旦排序,时间复杂度最好也就O(nlogn)了,想要O(n)就需要舍弃掉排序,想办法通过遍历来获得三个数的最大乘积
假设输入nums的长度为n,选择其中3个数相乘,即n个数内选3个数构成的组合,有C(n,3)种结果,当n
- 为3 时,C(3,3) 为1
- 为4 时,C(4,3) 为4
- 为5 时,C(5,3) 为10
- 为6 时,C(6,3) 为20
- 为7 时,C(7,3) 为35
- 为8 时,C(8,3) 为56
......
显然,在遍历中比较并保留最大三数乘积很麻烦,因此,在遍历中,应该记录最小两个数,以及最大三个数,在遍历后再计算结果、比较、得出最大者。寻找最小两数与最大三数的过程类似题414
需要稍微注意的一点是:最大三数与最小两数取值可能重叠
代码
class Solution {
public:
int maximumProduct(vector<int>& nums) {
if(3 == nums.size())
return nums[0] * nums[1] * nums[2];
long long llMaxValue = std::numeric_limits<long long>::max();
long long llMinValue = std::numeric_limits<long long>::min();
long long llMin = llMaxValue, llSecondMin = llMaxValue;
long long llMax = llMinValue, llSecondMax = llMinValue, llThirdMax = llMinValue;
for(const auto& elem : nums){
if(llSecondMin < elem && elem < llThirdMax) //过滤判定区间外的元素
continue;
if(elem > llMax){
llThirdMax = llSecondMax;
llSecondMax = llMax;
llMax = elem;
}else if(elem > llSecondMax){
llThirdMax = llSecondMax;
llSecondMax = elem;
}else if(elem > llThirdMax){
llThirdMax = elem;
}
if(elem < llMin){
llSecondMin = llMin;
llMin = elem;
}else if(elem <llSecondMin){
llSecondMin = elem;
}
}
return std::max(llMin * llSecondMin * llMax, llMax * llSecondMax * llThirdMax);
}
};
网友思路
在看了别人的提交后,我才知道排序不一定是O(nlogn),STL中部分排序nth_element,使用得当时间复杂度为O(n)。。。。。。。。
nth_element中文解释:https://zh.cppreference.com/w/cpp/algorithm/nth_element