LeetCode每日一题之三个数的最大乘积

前言

欢迎阅读LeetCode每日一题系列,这个系列将会以每日一道算法题的形式,给大家带来详细的LeetCode算法题的解题思路和编写过程,带你一路过关斩将,拿下这些看似难懂的算法题!今天给大家分享的是三个数的最大乘积!废话不多说,先上题目!

1.1 题目要求

题目类型:三个数的最大乘积

题目内容:整型数组nums,在数组中找出由三个数字组成的最大乘积,并输出这个乘积

注意事项:乘积不会越界

重点考查:线性扫描

1.2 解题方法

怎么样?看完了题目,是不是感觉一头雾水;没关系,看一看解题思路,相信你会有所启发!

1.2.1 解题思路

例如一个整型数组元素为:[1, 2, 3, 4 ] ,那么最大乘积就是 2 * 3 * 4 = 24

  • 若整型数组的元素都为整数,那么只需要求出三个大数的乘积,即为最大乘积;

注意这里三个大数是指在整型数组的中绝对值排在前三的元素

  • 若整型数组的元素都为负数,那么仍然需要求出三个大数的乘积

注意由于三个负数相乘,其结果仍为负数,负数的绝对值越大,其实际值越小,因此这里三个大数是指在整型数组的中绝对值排在倒数三位的元素

例如整型数组 nums的元素为:[-4, -3, -2, -1],求三个数的最大乘积时,选取的乘数是-3, -2, -1,而不是 -4, -3, -2

  • 若整型数组的元素有正有负时,则需要在负数中选择两个小数(即绝对值排在前两个的元素), 在正数中选择一个最大数;

因此可以分为两种情况:

  • 第一种情况:数组元素全为正数或者负数, 选取数组中排前三的元素,三者求乘积
  • 第二种情况:数组元素有正有负,选取数组中的负数绝对值排前二的元素, 正数中最大元素,三者求乘积
1.2.2 代码实现
1.使用快速排序查找

这里我们使用JDK1.8 API中提供的数组工具类提供的排序方法,即双轴快速排序算法

  • Arrays数组工具类的sort()方法源码
package java.util;
//数组工具类
public class Arrays {
    /**
     * 将指定的数组按升序排列
     *
     * 注意: 该排序算法为双轴快排 (由Vladimir Yaroslavskiy和Jon Bentley和Joshua Bloch编写)
     * 该算法在大规模数据下的时间复杂度为O(n log(n)), 导致其他快速排序降级为二次性能, 通常比传统的(单轴)快速排序实现更快
     *
     * @param 被排序的数组
     */
    public static void sort(int[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }
}
  • 测试代码
package com.kuang.leetcode6;

import java.util.Arrays;

/**
 * @ClassName MaxProduct
 * @Description 整型数组中三个数的最大乘积
 * @Author 狂奔の蜗牛rz
 * @Date 2021/8/18
 */
public class MaxProduct {

    //主方法测试
    public static void main(String[] args) {
        
        //初始化整型数组元素
        //第一种情况: 数组元素都为正数
        int[] arr1 = {1,2,3,4,5,6};
        //第一种情况: 数组元素都为负数
        int[] arr2 = {-6,-5,-4,-3,-2,-1};
        //第二种情况: 数组元素有负有正
        int[] arr3 = {-6,5,-4,3,-2,1};

        //调用sort方法(快速排序算法), 获取最大乘积
        System.out.println("使用快速排序算法:");
        //预估结果为: 4*5*6=120
        System.out.println(sort(arr1)); // 测试结果: 120
        //预估结果为: (-3)*(-2)*(-1)=-6
        System.out.println(sort(arr2)); // 测试结果: -6
        //预估结果为: (-6)*(-4)*5=120
        System.out.println(sort(arr3)); // 测试结果: 120

    }

    /**
     * 使用快速排序查找最大乘积
     * 将整型数组的元素进行排序, 并返回最大乘积
     * @param nums 待排序的整型数组
     * @return 最大乘积值
     */
    public static int sort(int[] nums) {
        /**
         * 直接使用Arrays数组工具类的sort方法
         * (底层是双轴快速排序算法, 其时间复杂度为O(n log(n)))
         */
        Arrays.sort(nums);
        /**
         * 有没有比它时间复杂度更低的算法?
         * 不使用排序, 使用线性扫描
         */
        //变量n为整型数组的长度
        int n = nums.length;
        //返回值为两种情况下乘积的最大值(使用Math的max方法求最大值)
        /**
         * 第一种情况, 数组元素中有正有负:
         * 经过快速排序后, 数组元素升序排列; 此时nums[0]中存放的负数中最小的元素,
         * nums[1]中存放的是负数中第二小的元素, 而nums[n-1]中存放的是正数中最大的元素,
         * 将三个元素求取乘积: 即 nums[0] * nums[1] * nums[n-1]
         * 第二种情况, 数组元素中只有正数或者负数:
         * 经过快速排序后, 数组元素升序排列,
         * 此时nums[n-1], nums[n-2]和nums[n-3]中存储的分别是数组元素中最大值, 次大值和第三大值;
         * 将三个元素求取乘积: 即 nums[n-1] * nums[n-2] * nums[n-3];
         *
         * 无论整型数组中全是正数或者负数, 或者有正有负, 我们只需将数组中这两种情况下的最大值分别求出,然后比较出真正的最大值
         */
        return Math.max(nums[0] * nums[1] * nums[n-1], nums[n-1] * nums[n-2] * nums[n-3]);
    }
    
}
  • 测试结果

在这里插入图片描述

结果测试结果与预期结果相同!

2.使用线性扫描查找
  • 测试代码
 package com.kuang.leetcode6;

import java.util.Arrays;

/**
 * @ClassName MaxProduct
 * @Description 整型数组中三个数的最大乘积
 * @Author 狂奔の蜗牛rz
 * @Date 2021/8/18
 */
public class MaxProduct {

    //主方法测试
    public static void main(String[] args) {
        
        //初始化整型数组元素
        //第一种情况: 数组元素都为正数
        int[] arr1 = {1,2,3,4,5,6};
        //第一种情况: 数组元素都为负数
        int[] arr2 = {-6,-5,-4,-3,-2,-1};
        //第二种情况: 数组元素有负有正
        int[] arr3 = {-6,5,-4,3,-2,1};
        
        //调用getMaxMin方法(线性查找算法), 获取最大乘积
        System.out.println("使用线性扫描算法:");
        //预估结果为: 4*5*6=120
        System.out.println(getMaxMin(arr1)); // 测试结果: 120
        //预估结果为: (-3)*(-2)*(-1)=-6
        System.out.println(getMaxMin(arr2)); // 测试结果: -6
        //预估结果为: (-6)*(-4)*5=120
        System.out.println(getMaxMin(arr3)); // 测试结果: 120
    }

    /**
     * 双轴快速排序的时间复杂度为O(n log(n)), 有没有比它时间复杂度更低的算法?
     * 解决方案: 不使用排序, 使用线性扫描, 时间复杂度为O(n)
     */

    /**
     * 使用线性扫描查找最大乘积
     * @param nums 待排序的整型数组
     * @return 最大乘积值
     */
    public static int getMaxMin(int[] nums) {
        /**
         * 变量min1表示整型数组中第一小元素值, min2表示第二小元素值;
         * 注意: 这里min1和min2虽然表示小元素值, 但是其初值却为Integer.MAX_VALUE
         */
        int min1 = Integer.MAX_VALUE, min2 = Integer.MAX_VALUE;
        /**
         * max1,max2和max3分别表示整型数组中第一大, 第二大和第三大元素值;
         * 注意: 这里max1, max2和max3虽然表示大元素值, 但是其初值却为Integer.MIN_VALUE
         */
        int max1 = Integer.MIN_VALUE, max2 = Integer.MIN_VALUE, max3 = Integer.MIN_VALUE;
        //遍历nums整型数组, x为整型数组中的每个元素值
        for (int x : nums) {
            //判断x(当前元素值)是否小于min1(默认第一小值)
            if(x < min1) {
                //若x值小于min1, 将min1(第一小值)赋值给min2(第二小值)
                min2 = min1;
                //然后将x(真实最小值)赋值给min1(默认第一小值)
                min1 = x;
            //若x值大于min1, 则继续判断x(当前元素值)是否小于min2(默认第二小值)
            } else if(x < min2) {
                //若x值小于min2, 则将x(实际第二小值)赋给min2(默认第二小值)
                min2 = x;
            }
            //判断x(当前元素值)是否大于max1(默认第一大值)
            if(x > max1) {
                //若x大于max1, 将max2(默认第二大值)赋值给max3(默认第三大值)
                max3 = max2;
                //然后将max1(默认第一大值)赋值给max2(默认第二大值)
                max2 = max1;
                //最后将x(当前元素值)赋值给max1(默认第一大值)
                max1 = x;
            //若x小于max1, 判断x(当前元素值)是否大于max2(默认第二大值)
            } else if( x > max2) {
                //若x大于max2, 将max2(默认第二大值)赋值给max3(默认第一大值)
                max3 = max2;
                //然后将x(当前元素值)赋值给max2(默认第二大值)
                max2 = x;
            //若x小于max2, 判断x(当前元素值)是否大于max3(默认第三大值)
            } else if( x > max3) {
                //若x大于max2, 将x(当前元素值)赋值给max3(默认第三大值)
                max3 = x;
            }
        }
        //最后将两种情况下的最大乘积比较, 将实际最大乘积进行返回
        return Math.max(min1 * min2 * max1, max1 * max2 * max3);
    }
  • 测试结果

在这里插入图片描述

结果测试结果与预期结果相同!


好了,今天LeetCode每日一题—三个数的最大乘积到这里就结束了,欢迎大家学习和讨论,点赞和收藏!


参考视频链接:https://www.bilibili.com/video/BV1Ey4y1x7J3 (国内算法宝典-LeetCode算法50讲)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狂奔の蜗牛rz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值