LeetCode 238. 除自身之外數組的乘積 | Python

238. 除自身之外數組的乘積


題目來源:力扣(LeetCode)https://leetcode-cn.com/problems/product-of-array-except-selfpython

題目


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

示例:bash

輸入: [1,2,3,4]
輸出: [24,12,8,6]

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

說明: 請不要使用除法,且在 O(n) 時間複雜度內完成此題。code

進階:blog

你能夠在常數空間複雜度內完成這個題目嗎?( 出於對空間複雜度分析的目的,輸出數組不被視爲額外空間。)索引

解題思路


思路:左右乘積列表leetcode

先看題目的提示,保證數組中任意元素的所有前綴後綴(甚至整個數組)的乘積都在 32 位整數範圍內,那這裏就能夠不考慮數據溢出的問題。rem

再看說明,這個說明中表示,不可以使用除法。由於,若是要求除當前元素數組的乘積,只要先求得整個數組的乘積,除以當前元素,那麼就是要求的答案。(固然,這裏有個問題,若是當前元素是 0 的話,這裏就要注意。不過題目不建議往這個方向考慮解決的方法,那麼這裏就不展開去說明了。)get

如今看本篇幅使用的方法:左右乘積列表,這裏須要先構造 left,right 兩個數組,分別存儲當前元素左側的乘積以及右側的乘積。

具體的構造方法:

  • 初始化兩個數組 leftright。其中 left[i] 表示 i 左側所有元素的乘積。right[i] 表示 i 右側所有元素的乘積。
  • 開始填充數組。這裏須要注意 left[0]right[lenght-1] 的值(length 表示數組長度,right 數組是從右側往左進行填充)。

    • 對於 left 數組而言,left[0] 表示原數組索引爲 0 的元素左側的全部元素乘積,這裏原數組第一位元素左側是沒有其餘元素的,因此這裏初始化爲 1。而其餘的元素則爲:left[i] = left[i-1] * nums[i - 1]
    • 一樣的 right 數組,right[length-1] 表示原數組最末尾的元素右側全部元素的乘積。但由於最末尾右側沒有其餘元素,因此這裏 right[length-1] 也初始化爲 1。其餘的元素則爲:right[i]=right[i+1]*nums[i+1]
  • 至於返回數組 output 數組,再次遍歷原數組,索引爲 i 的值則爲:output[i] = left[i] * right[i]

主要在於 left 和 right 數組的構造,具體的過程以下圖:

構建數組圖解

具體的實現代碼見【code 1】 部分。

上面的方法實現以後,時間複雜度爲 O(N),空間複雜度也是 O(N) (N 表示數組的長度)。由於 構造 left 和 right 數組,二者的數組長度就是 N。

題目中的進階部分,但願可以嘗試使用常數空間複雜度完成本題。(這裏不計輸出數組的空間)

方法仍是使用 左右乘積列表 的方法,可是這裏不在單獨構建 left 和 right 數組。直接在輸出數組中進行構造。

具體的思路:

  • 先將輸出數組當成 left 數組進行構造
  • 而後動態構造 right 數組計算結果

具體的作法:

  • 先初始化 output 數組,先當成 left 數組進行構造,那麼 output[i] 就表示 i 左側全部元素的乘積。(具體的構造方法同上)
  • 可是,這裏咱們不可以再單獨構造 right 數組(前面說了,空間複雜度須爲常數)。這裏咱們使用的方法是,在遍歷輸出答案的時候,維護一個變量 right,而變量 right 表示右側元素的乘積。遍歷更新 output[i] 的值爲 output[i] * right,同時更新 right 的值爲 right * nums[i] 表示遍歷下一個元素右側的元素乘積。

具體實現代碼見【code 2】。

代碼實現


# code 1
class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        length = len(nums)
        
        left = [0] * length
        right = [0] * length
        output = [0] * length

        # 先填充 left 數組
        # left 數組表示遍歷時 i 左側的乘積
        # 由於數組第一個元素左側沒有其餘元素,因此 left 數組第一個元素爲 1
        # left 數組接下來的元素則爲原數組的第一位元素與 left 數組第一位元素的乘積,依次類推
        left[0] = 1
        for i in range(1, length):
            left[i] = nums[i-1] * left[i-1]

        # 一樣的 right 數組從右往左進行填充
        # 一樣數組末尾元素右側沒有其餘元素,因此末尾元素值爲 1
        # 右邊往左的元素則爲原數組與 right 數組末尾往前一位元素的乘積,依次類推
        right[length-1] = 1
        for i in range(length-2, -1, -1):
            right[i] = nums[i+1] * right[i+1]
        
        # 從新遍歷,輸出 output 數組
        # output[i] 等於 nums 中除 nums[i] 以外其他各元素的乘積
        # 也就是 output[i] 的值爲 left[i] * right[i]
        for i in range(length):
            output[i] = left[i] * right[i]
        
        return output

# code 2
class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        length = len(nums)
        output = [0] * length

        # 先構建 output 爲左側乘積列表
        # 一樣初始化第一個元素爲 1
        output[0] = 1
        for i in range(1, length):
            output[i] = output[i-1] * nums[i-1]
        
        # 維護一個變量 right
        # 變量更新 output[i] 的值爲 output[i] * right
        # 同時更新 right = right * nums[i] 表示遍歷到下個元素右側的乘積(此時遍歷從右向左)
        # 初始化 right 爲 1
        right = 1
        for i in range(length-1, -1, -1):
            output[i] = output[i] * right
            right = right * nums[i]
        
        return output

實現結果


【code 1 實現結果】

code 1 實現結果

【code 2 實現結果】

code 2 實現結果

總結


  • 先閱覽題目,排除使用除法的方法。使用左右乘積列表的方法來解決問題。
  • 肯定方法後,進行構建 left 和 right 數組。構造時須要注意的是 left[0]right[length-1] 兩個元素。

    • left[0] 表示原數組索引爲 0 左側全部元素的乘積。而原數組索引爲 0 的元素左邊沒有元素,因此初始化爲 1。而其餘元素則爲:left[i] = left[i-1] * nums[i-1]
    • 一樣 right[length-1] 表示原數組末尾元素右側全部乘積。這裏元素組最末尾的元素右側沒有其餘元素,因此 right[length-1] 也初始化爲 1。
  • 返回的輸出數組 output 則爲:output[i] = left[i] * right[i]
  • 前面的方法足以解決問題,題目要求進階嘗試使用常數的空間複雜度解決問題。這裏仍是使用左右乘積列表的思路。(題目中說明輸出數組不視爲額外空間)那麼先用輸出數組構造左乘積列表,維護一個變量 right。
  • 遍歷更新 output 的數組,更新值爲 output[i] * right(注意:這裏遍歷時從右往左遍歷),更新 output 的值的同時,更新 right 的值爲 right * nums[i],這裏表示下一個遍歷的值右側全部元素的乘積。
相關文章
相關標籤/搜索