• 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





    原创不易,转载请注明出处,谢谢
  • 相关阅读:
    gcc编译器创建和使用静态库、动态库
    shared_ptr & unique_ptr & weak_ptr (C++11)
    MyString(重写String)
    Linux进程间通讯的几种方式的特点和优缺点,和适用场合
    按行N等分某个文件
    Hbase region 某个regionserver挂掉后的处理
    gentoo
    Hbase 常用shell命令
    网络爬虫速成指南(二)网页解析(基于算法)
    hbase 0.96 java 示例
  • 原文地址:https://www.cnblogs.com/Keeping-Fit/p/14660280.html
Copyright © 2020-2023  润新知