【力扣】238:除自身以外數組的乘積 | 動態規劃

題目描述

給你一個長度爲 n 的整數數組 nums,其中 n > 1,返回輸出數組 output ,其中 output[i] 等於 nums 中除 nums[i] 之外其餘各元素的乘積。

提示:題目數據保證數組之中任意元素的全部前綴元素和後綴(甚至是整個數組)的乘積都在 32 位整數範圍內。

要求:在 O(n) 時間複雜度內完成此題。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/product-of-array-except-self
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

算法

第一個想到的最簡單的方法:

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        n=1 # 保存整個數組除0以外的元素乘積
        k=0 # 保存數組中0的數量
        for i in nums:
            if i:
                n*=i
            else:
                k+=1
        if k>=2:
            return [0]*len(nums)
        if k==1:
            ls=[]
            for i in nums:
                if i:
                    ls.append(0)
                else:
                    ls.append(n)
        else:
            ls=[]
            for i in nums:
                if i:
                    ls.append(n//i)
        return ls

這種方法需要兩次遍歷,時間複雜度是O(n),需要O(n)的空間複雜度,但是第一次需要兩個變量保存值,第二次遍歷所使用的空間其實可以用原數組。

要求:禁止使用除法

# 動態規劃,開闢兩類空間,分別存儲索引i左邊(右邊)所有數的乘積,
# 所以,output[i]即爲i左邊所有數的乘積*i右邊所有數的乘積.

大佬就是大佬,一句話就豁然開朗。

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        n = len(nums)
        # 動態規劃,開闢兩類空間,分別存儲索引i左邊(右邊)所有數的乘積,
        # 所以,output[i]即爲i左邊所有數的乘積*i右邊所有數的乘積.
        left=nums.copy()
        right=nums.copy()
        for i in range(1,n):
            left[i]*=left[i-1]
        for i in range(n-2,-1,-1):
            right[i]*=right[i+1]
        res=[]
        for i in range(n):
            res.append((left[i-1] if i>=1 else 1)*(right[i+1] if i+1<n else 1))
        return res

執行用時 :88 ms, 在所有 Python3 提交中擊敗了17.32%的用戶
內存消耗 :19.7 MB, 在所有 Python3 提交中擊敗了9.09%的用戶

進階!

你可以在常數空間複雜度內完成這個題目嗎?( 出於對空間複雜度分析的目的,輸出數組不被視爲額外空間。)
對上面的算法進行優化,事實是我們不需要左右兩個數組來保存乘積,只需要其中一個即可。在計算另一個數組時就可同時計算出結果數組。

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        n = len(nums)
        # 動態規劃,開闢兩類空間,分別存儲索引i左邊(右邊)所有數的乘積,
        # 所以,output[i]即爲i左邊所有數的乘積*i右邊所有數的乘積.
        # 爲了滿足O(1)空間複雜度要求(輸出數組不被視作額外空間),
        # left和right數組只保留一個,爲了方便計算,最好是保留right
        
        right=nums.copy()
        for i in range(n-2,-1,-1):
            right[i]*=right[i+1]
        res=[]
        left=1
        for i in range(n-1):
            res.append(left*right[i+1])
            left*=nums[i]
        res.append(left)
        return res

執行用時 :64 ms, 在所有 Python3 提交中擊敗了56.66%的用戶
內存消耗 :18.4 MB, 在所有 Python3 提交中擊敗了100.00%的用戶

再進階!

你可以在常數空間複雜度內完成這個題目嗎?

寫出如上的算法時又突然發現,其實可以實現真的**在常數空間複雜度內完成這個題目!**因爲:

        res=[]
        left=1
        for i in range(n-1):
            res.append(left*right[i+1])
            left*=nums[i]
        res.append(left)

這裏的結果數組res其實不是必須的,我們完全可以用原數組nums替代,那麼如果要替代問題是什麼?
在下面的left*=nums[i]會被覆蓋掉,顯然,用一個額外的變量提前保存會被覆蓋的位置的值即可!

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        n = len(nums)
        # 動態規劃,開闢兩類空間,分別存儲索引i左邊(右邊)所有數的乘積,
        # 所以,output[i]即爲i左邊所有數的乘積*i右邊所有數的乘積.

        right=nums.copy()
        for i in range(n-2,-1,-1):
            right[i]*=right[i+1]
        res=[]
        left=1
        pre=nums[i]
        for i in range(n-1):
            
            nums[i]=left*right[i+1]
            left*=pre
            pre=nums[i+1]
        nums[-1]=left
        return nums

執行用時 :60 ms, 在所有 Python3 提交中擊敗了70.12%的用戶
內存消耗 :17.5 MB, 在所有 Python3 提交中擊敗了100.00%的用戶

參考算法

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        n = len(nums)
        # 動態規劃,開闢兩類空間,分別存儲索引i左邊(右邊)所有數的乘積,
        # 所以,output[i]即爲i左邊所有數的乘積*i右邊所有數的乘積.
        # 爲了滿足O(1)空間複雜度要求(輸出數組不被視作額外空間),
        # 首先,一類空間的dp數據(i右邊所有數的乘積)用輸出數組存儲,
        # 二類空間的dp數據(i左邊所有數的乘積)用兩個變量存儲,
        # 因爲這兩個變量會一直變化, 所以應該實時更新輸出數組.
        dp_right = [1 for i in range(n)]
        pre_left = 1
        for i in range(n - 2, -1, -1):
            dp_right[i] = dp_right[i + 1] * nums[i + 1]
        for i in range(1, n):
            cur_left = pre_left * nums[i - 1]
            dp_right[i] = cur_left * dp_right[i]
            pre_left = cur_left
        return dp_right

拓展

最後補充一個很酷的拓展:
官方的一句話系列:

import functools
class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        return list(map(lambda x,y:x*y,functools.reduce(lambda x,y:x.append(x[-1]*y) or x,nums[:-1],[1]),functools.reduce(lambda x,y:x.append(x[-1]*y) or x,nums[-1:0:-1],[1])[::-1]))
import functools
class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        return list(
            map(
                lambda x,y:x*y,
                functools.reduce(
                    lambda x,y:x.append(x[-1]*y) or x,nums[:-1],[1]),
                    functools.reduce(
                        lambda x,y:x.append(x[-1]*y) or x,nums[-1:0:-1],[1])[::-1]))

分析了一下,是利用標準庫functools,這裏順便提一句以前內置函數裏好像是有reduce的,但後來就沒了,這裏用functools.reduce()做替代。

lambda函數:lambda x,y:x.append(x[-1]*y) or x

但是我貌似還是沒看懂算法的構建啊,吐血。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章