蠻多 coding 語言在一般情況下是使用 32 bits 的空間來儲存整數的,例如 Java 的 int,範圍是 -2147483648 ~ 2147483647,大約正負21億。但是現實世界中,較小的數字往往比較常出現,大約幾十到幾十萬是最多最常出現的,如果只是要儲存或傳輸這樣的一個小數字,卻每次都需要用到 32 bits 的空間,其實有點浪費,這是有機會優化的 !

Varint 和 Zigzag 演算法就是要處理這種問題,讓值小的數字,可以用較少的 byte 數量表示,而達到資料壓縮目的,著名的資料傳輸格式 Protobuf 也是通過 Varint 和 Zigzag ,來大幅減少了資料佔用的空間。

Continue reading

Reservoir sampling

Reservoir sampling 是一個隨機演算法,其目的是在只遍歷一遍的情況下,從大數據 N 的資料流中,隨機選取出 k 個元素,且每筆資料選中的機率都要一樣。這個場景強調了幾件事:

  • 集合 N 很大且不可知,所以不能一次存入記憶體
  • 時間複雜度為 O(N)
  • 隨機選取 k 個數,每個數被選中的機率為k/N

本來面對這種問題,比較直接的想法是利用隨機數演算法,求 random(N) 得到隨機數,但是因資料流極大,無法一次都讀到記憶體內,這就表示不能像數組一樣根據 index 獲取元素;而且題目強調只能遍歷一遍O(N),代表也不能再採用分塊方式儲存資料,之後再隨機遍歷。為了解決這個問題,可以使用 Reservoir sampling ,非常的巧妙。

Continue reading

Backtrack 是 DFS 的一種形式,基本寫法類似於 TOP DOWN DFS,處理方式就是所謂的窮舉法,將所有可能的結果都找出來;每一個結果都實際看看這樣。換個角度來說,其實這個過程就如同在樹上遍歷 (Tree Traversal) ,而普通的 DFS 是不需要回溯狀態的。 Backtrack 強調了狀態回溯。

Continue reading

Merge Sort

歸併排序 (Merge Sort) 算是比較優秀的排序算法,因為時間複雜度是 O(N log N);而選擇排序、冒泡排序、和插入排序時間複雜度則是O(N^2)。 Merge Sort 的基本思想是分治法 (Divide and conquer),是將原問題分解為規模較小的子問題,然後逐一解決這些子問題之後,合併這些子問題的答案,並建立原問題的答案。

Continue reading

Recursion

遞迴 (Recursion),通過調用自身函數,有時可以將某個複雜的問題,分解為規模較小的子問題。而 Recursion 也經常和以下演算法配合使用 :

  • 分治法 divide and conquer
  • 回溯法 backtrack
  • 動態規劃 dynamic programming

在實際狀況中,遞迴函數的設計常常很難想像,因為遞迴設計屬於逆向思維,在設計遞迴函數的時候,非常容易被這種層層嵌套搞暈,在這裡簡單給個模板範例講解。

Continue reading

Complexity Analysis

在刷 leetcode 時,都會需要分析和解釋一下自己寫的程式的演算法複雜度,基本上主要是解釋 :

  • 時間複雜度
  • 空間複雜度

從以上兩個面相去分析 code 效率、品質、trade off,本文記錄一些分析要點。另外建議面試的時候,當對題目有想法時,先不用急著直接實作,而是先估出複雜度,並和面試人討論,確認不會 TLE ;空間是否需要優化;符不符合題目要求等等,最後再開始寫。

Continue reading

Author's picture

李昀陽 YunYang Lee

Welcome to my Tech Note. You can read some of the chapters below.

Software Engineer

Taiwan