算法對程序運行速度有著怎樣的影響?這或許是一個值得關心的問題。
1 題目 Q1314 矩陣塊求和
給定一個 mxn 矩陣 mat 和整數 K,返回矩陣 answer,其中每個 answer[i][j]是 i-K <= r <= i + K 的所有元素 mat[r][c] 的和。j - K <= c <= j + K,並且(r,c)是矩陣中的有效位置。
約束條件:
(a) m == mat.length
(b) n == mat[i].length
(c) 1 <= m, n, K <= 100
(d) 1 <= mat[i][j] <= 100
示例1:
<code>Input: mat =
[[1,2,3],[4,5,6],[7,8,9]]
, K =1
Output:[[12,21,16],[27,45,33],[24,39,28]]
/<code>輸入一個 的矩陣,並指定領域大小 K=1,計算輸出結果的過程如圖:
示例1
示例2:
<code>Input: mat =
[[1,2,3],[4,5,6],[7,8,9]]
, K =2
Output:[[45,45,45],[45,45,45],[45,45,45]]
/<code>輸入一個 3x3 的矩陣,並指定領域大小 K=2,計算輸出結果的過程如圖:
示例2
2 直接求解
對於這樣的問題,我們可以根據要求和矩陣塊求和的定義直接求解。大體的思路是:(1) 遍歷每一個元素,(2) 對每一個元素計算領域內所有元素的和。 這種思路的圖示過程已經在 “示例1” 和 “示例2” 中表現了。對於邊長為 n 的方陣,在領域 K 的情況下,時間複雜度為 O(n^2K^2) 。在最壞情況下,即 n=K 時,時間複雜度為 O(n^4)。
這種直接求解思路的C++解決方案如下:
在Leetcode網站評測中,這種方案的耗時為 672ms,可以記住這個數據,待會兒與另一個解決方案的評測進行比較。
3 積分圖解法
我們可以考慮這樣一個問題:直接求解的思路中是否存在很多重複的計算?只要 K>0,就會出現重疊區域,那麼關於這些重疊區域的求和計算就是重複計算。如何來避免或者減少這些重複計算就成了我們優化的關鍵。
這正是積分圖的用武之地。積分圖是指矩陣的一個位置存儲的值為從原點到該位置的子矩陣的所有元素的求和。 如果要求解原矩陣中一個子矩陣塊的和,在積分圖中,只需獲取這個矩陣塊的四個角的元素,然後進行加加減減的計算即可。具體操作如圖所示:
積分圖使用示意圖
針對 Q1314 這個問題設計的解決思路如下:(1) 根據原始矩陣創建積分圖,(2) 對矩陣的每一個元素位置進行遍歷,(3) 結合領域 K 計算矩陣塊的四個角的索引座標,假設分別為 (1,1), (1,0), (0,1), (0,0),(4) 根據公式計算矩陣塊的和
sum = m(1,1) - m(1,0) - m(0,1) + m(0,0)
算法操作的示意圖如下:
積分圖求解示意圖
對積分圖求解的思路進行分析,針對邊長為 n 的方陣,無論 K 的取值,時間複雜度都是 O(n^2)。 這相比與直接求解的方式顯然是好了很多。
根據這種積分圖的求解思路實現的C++解決方案如下:
Leetcode 網站上的評測顯示,積分圖解決方案耗時為 24ms,也就是相比較於直接求解的方法,速度提升了二十多倍。這在算法優化中確實是不多見的優化收益,對於一個常常需要計算矩陣塊求和的問題有很好的加速效果。
-----------------------
題外
頻道資源,可以私信關鍵字獲取。
Python編程問題諮詢
獲取leetcode源代碼,請發送關鍵字【leetcode】
獲取書籍,請發送關鍵字【書籍】