子数组的最大乘积 (python)

给定一个长度为N的整数数组(含正数和负数),只允许用乘法,不能用除法,计算(N-1)个数的组合中乘积最大的一组。

解法 1

暴力写法很容易就可以写出来,就是把所有的(N-1)个数组合找出来,分别计算它们的乘积,并计算大小。由于总共有N个(N-1)个数的组合,总的时间赋值度是O(N*N)。

array = [i for i in range(-8,8)]
ans = -2**31
def product(nums): # 乘积
    res = 1
    for i in range(len(nums)):
        res *= nums[i]
    return res
def conbination(track, start, array): # 组合
    global ans
    if len(track) == len(array)-1:
        ans = max(ans, product(track))
        return
    for i in range(start, len(array)):
        track.append(array[i])
        conbination(track,i+1,array)
        track.pop()
conbination([],0,array)
print(ans)

暴力法还有一种做法:就是用N个数的乘积除以整数数组中的每个数。但这种做法的前提条件是整数数组不能含有0,因为含有0的化N个数的乘积就会等于0。

解法 2

解法1显然不是最好的解法。在计算机科学中,时间和空间往往是一对矛盾体,不过这里有一个折中的方法。可以通过“空间换时间”或时间换空间的策略来达到优化某方面的目的。在这里是否可以通过“空间换时间”来降低时间复杂度呢?计算(N-1)个数的组合,假设第i个(0<=i<=N-1)个元素被排除在乘积之外。

设array[]为初始数组,s[i]为前i个元素的乘积其中1<=i<=N,s[0] = 1为边界条件,那么有s[i] = s[i-1]*array[i-1]。设t[i]为数组后(N-i)个元素的乘积。那么t[i] = t[i+1]*array[i],其中1<=i<=N,t[N+1] = 1为边界条件。那么设p[i]为数组除第i个元素外,其他N-1个元素的乘积,即有:

p[i] = s[i-1]*t[i+1]

该算法的时间复杂度为O(N)。

array = [i for i in range(-8,8)]
n = len(array)
s, t, p = [1]+[0]*(n), [0]*(n) + [1], [0]*(n)
# s[i]表示前i个元素的乘积
for i in range(1,n+1):
    s[i] = s[i-1]*array[i-1]
# t[i]表示数组后(N-i)个元素的乘积
for i in range(n-1,-1,-1):
    t[i] = t[i+1]*array[i]
# p[i]为数组除第i个元素外,其他N-1个元素的乘积
for i in range(n):
    p[i] = s[i-1]*t[i+1]
print(max(p))

解法 3

可以通过分析正负符号来进一步减少解答问题的计算量。我认为这是在找规律。假设N个整数的乘积为P。针对P的正负性进行如下分析(其中A(N-1)表示N-1个数的组合,P(N-1)表示N-1个数的组合乘积)

P = 0

那么,数组中至少包括一个0。假设除去一个0之外,其他N-1个数得乘积为Q,根据Q的正负号来进行判断:

  • Q为0 说明数组中至少有两个0,所以返还的乘积为0
  • Q为正数 返还Q
  • Q 为负数 返还0

P 为负数

根据“负负的正”的乘法性质,从N个数中去掉一个负数,使得P(N-1)为正数。这个去掉的负数的绝对值是最小的。

P为正数

如果数组中存在正数,那么去掉最小的正数值,否则去掉绝对值最大的负数。

时间复杂度为O(N)。

array = [i for i in range(-8,8)]
Num0 = [] # 0的个数,直接保存索引
NumNagative = [] # 负数的个数,直接保存索引
NumPositive = [] # 正数的个数,直接保存索引
for i in range(len(array)):
    if array[i] == 0:
        Num0.append(i)
    elif array[i] < 0:
        NumNagative.append(i)
    else:
        NumPositive.append(i)
# 如果P = 0
if len(Num0) >= 1:
    if len(Num0) > 1 or len(NumNagative)%2 == 1:
        print(0)
    else:
        P = 1
        for i in range(len(array)):
            if i != Num0[-1]:
                P *= array[i]
        print(P)
# 如果P为负数
elif len(NumNagative)%2 == 1:
    # 找到NumNagative绝对值的最小的值
    AbsMin = array[NumNagative[0]]
    for i in range(1,len(NumNagative)):
        if abs(array[NumNagative[i]]) <= abs(AbsMin):
            AbsMin = array[NumNagative[i]]
    P = 1
    for i in range(len(array)):
        if array!=AbsMin:
            P *= array[i]
    print(P)
# P为正数
else:
    if len(NumPositive)!= 0:
        NumMin = array[NumPositive[0]]
        for i in range(1,len(NumPositive)):
            if NumMin >= array[NumPositive[i]]:
                NumMin = array[NumPositive[i]]
        P = 1
        for i in range(len(array)):
            if array != NumMin:
                P *= array[i]
        print(P)
    else:
        AbsMax = array[NumNagative[0]]
        for i in range(1, len(NumNagative)):
            if abs(array[NumNagative[i]]) >= abs(AbsMax):
                AbsMax = array[NumNagative[i]]
        P = 1
        for i in range(len(array)):
            if array != AbsMax:
                P *= array[i]
        print(P)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值