628.三个数的最大乘积

toc

题目

给你一个整型数组 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个数最大的乘积。
由于:

  1. 越大的非负数乘积越大,所以尽量取最大的3个非负数
  2. 正数 > 0 > 负数,负负才能得正,若有负数时应尽量取2个负数
  3. 负数的数值部分越大,负数越小,仅有时应取数值部分较小的负数(即较大的负数)

所以最大的三个数之积,较可能偏向,2个最小负数之积再乘最大非负数,或三个最大非负数之积
分三种情况讨论:

  • 负数2个以下,即输入全为非负数、或有1个负数的情况(负负得正,仅有一个负没法得到正)
    直接取3个最大的非负数之积
  • 输入全为负数
    直接取3个最大的负数之积
  • 输入负数与非负数均有
    • 非负数大于0个但小于3个,负数含2个及以上(必然会用到最少一个负数,负负才能得正,所以用两个负数)
      直接取最小两个负数之积 * 最大非负数
    • 非负数含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





posted @ 2021-04-14 22:34  無雙  阅读(127)  评论(0编辑  收藏  举报