zoukankan      html  css  js  c++  java
  • 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





    原创不易,转载请注明出处,谢谢
  • 相关阅读:
    .net core读取appsettings.config中文乱码问题
    vs2017错误:当前页面的脚本发生错误
    VS Code中无法识别npm命令
    Visual Studio报错/plugin.vs.js,行:1074,错误:缺少标识符、字符串或数字
    记录一次在生成数据库服务器上出现The timeout period elapsed prior to completion of the operation or the server is not responding.和Exception has been thrown by the target of an invocation的解决办法
    Java集合框架
    java hash表
    Java Dictionary 类存储键值
    java数据结构 栈stack
    java封装
  • 原文地址:https://www.cnblogs.com/Keeping-Fit/p/14660280.html
Copyright © 2011-2022 走看看