稀疏矩陣的概念
一個m×n的矩陣是一個由m行n列元素排列成的矩形陣列。矩陣裏的元素可以是數字、符號及其他的類型的元素。
一般來說,在矩陣中,若數值爲0的元素數目遠遠多於非0元素的數目,並且非0元素分佈沒有規律時,則稱該矩陣爲稀疏矩陣;與之相反,若非0元素數目佔大多數時,則稱該矩陣爲稠密矩陣。定義非零元素的總數比上矩陣所有元素的總數爲矩陣的稠密度。,下面的矩陣就是一個典型的稀疏矩陣。
我們日常使用的電子表格也是一個典型的稀疏矩陣場景,電子表格(SpreadJS, Excel,Google Sheet等等),整體看起來像一個table表格。,
其中列名稱依次爲A, B, C … …, 行名稱依次爲1, 2, 3 … …
舉例一個比較極端的場景,在A1和ZZ2000單元格分別賦值,這樣我們就需要一個2000行,26*26+26=702列的矩陣來表示它,這個矩陣是一個明顯的稀疏矩陣。
稀疏矩陣的存儲方式及優化
直接存儲爲二維矩陣
直接使用二維矩陣會簡單直接地存儲整個電子表格,這樣你不必每次都創建或刪除一段內存。
但這是一種非常暴力的存儲值的方法,這種方式下會消耗大量內容來存儲毫無內容的單元格。
簡單的來看一下它的複雜度:
- 佔用空間:O(N2)
- 插入數據:需要破壞矩陣.
- 刪除數據:需要破壞矩陣.
- 搜索數據:O(N2)
- 訪問數據:O(1)
N是假設行和列具有相同長度並形成正方形矩陣的行/列數。
通過鍵值對(Map, Dictionary)優化
在這種方法中,只有在單元格有值時,我們纔將單元格的值和位置存儲在一起,使用HashMap或者Dictionary這些數據結構可以很容易的做到.。
下圖我們可以看到,鍵值對中分別存儲了單元格位置和單元格值。
來看一下它的複雜度:
- 空間:O(N)
- 插入:O(1)
- 刪除:O(1)
- 搜索:O(N)
- 訪問:O(1)
N爲所記錄的條目數。
通過稀疏矩陣存儲方式優化
在稀疏矩陣中,我們可以使用三個不同的數組來存儲行索引、列偏移、和其中的值,而不是直接在二維矩陣中存儲值。以這種方式按列壓縮稀疏矩陣
存儲的三個數組:
- 值 =>單元格中的值。
- 行索引=>單元格的行索引。
- 列偏移=>這裏每個索引都代表列,並且該數組將行開始的索引值存儲在 Row 數組中。
稀疏矩陣具體的插入,、刪除,、搜索,、訪問的代碼,大家可以自己來搜索,這方面的資料網上有很多。,這裏不一一列舉。
和上面一樣,來看看這種方式的複雜度:
- 空間:O(N)
- 插入:O(N)
- 刪除:O(N)
- 搜索:O(N)
- 訪問:O(1)
相較於傳統的數組存儲或是鍵值對存儲,稀疏矩陣存儲構建了基於行索引爲 Key 的數據字典,在鬆散佈局的表格數據中,稀疏矩陣只會對非空數據進行存儲,而不需要對空數據開闢額外的內存空間。使用這種特殊的存儲策略,使得數據片段化變得容易,可以隨時框取整個數據層中的一片數據,進行序列化或反序列化。如果我們在項目開發中需要存儲類似結構的數據,稀疏矩陣這種存儲方式,無論從時間還是空間上都能大大的提成性能。
在葡萄城的 SpreadJS 和 GcExcel 表格組件中,也巧妙的使用了稀疏矩陣這一特性,可以隨時替換或恢復整個存儲結構中的任何一個級別的節點,以改變引用的方式更高效的地解決表格數據回滾和恢復問題,而這一點也爲葡萄城表格組件支持多人在線協同打下了一個良好的基礎。
由於底層採用了稀疏矩陣來優化存儲,SpreadJS在前端頁面中,實現了100W級別數據秒級響應的能力:
純前端百萬級數據請求、加載、篩選和排序
點擊此處可以在線體驗:性能演示
同時,結合SpreadJS中使用的Canvas 繪製模型,雙緩存畫布渲染等專利技術,讓SpreadJS的前端性能相比於同類產品遙遙領先。
更多純前端表格在線demo示例 :https://demo.grapecity.com.cn/spreadjs/gc-sjs-samples/index.html
純前端表格應用場景:https://www.grapecity.com.cn/developer/spreadjs#scenarios
移動端示例(可掃碼體驗):http://demo.grapecity.com.cn/spreadjs/mobilesample/