在unix/linux的世界裡面,如果我們需要比較兩個檔案,就會用乙個比較的命令——diff。而這個diff的原理是什麼呢?
在diff裡面,我們比較的兩個檔案叫做old和new,而一般是按行來比較。這裡我們可以抽象成乙個字串的比較,比如:
old: abcdefger
new: abdefereger
那麼其中的每乙個字元都可以表示檔案裡面的一行。那麼diff裡面用到的比較思想是從old和new裡面找出最長的subsequence。
subsequence的定義是:如果原串是(a1, a2, ..., an)的話,其中ai表示串中的第i個字元,那麼(a[m1], a[m2], ..., a[mi])稱為乙個subsequence,如果m1, m2, ..., mi是[1, n]的元素,m1 < m2 < ...< mi且a[mi] 屬於ai裡面的字元。
那麼diff的任務就是找出old和new裡面的最長公共subsequence(longest common subsequence/lcs)。
for example:
old: fabc
new: ebca
那麼lcs就是bc。
那麼就可以設計乙個函式lsp,他接受兩個字串作為引數,返回他們的lsp。
string lsp(const string &s1, const string &s2);
同時,在我們對比字串的時候,我們都是從前往後的對比的,那麼我們可以得到下面的性質。
lsp(s1, s2) = 1). s1[0] + lsp(tail(s1), tail(s2)) if s1[0] == s2[0]
2). max if s1[0] != s2[0]
其中tail(str)表示str的除了第乙個字元之外的剩下的子串。
可以看出來上面的公式是乙個遞迴的suboptimal公式,這正是dynamic programming(dp)裡面的乙個思想。所以我們就可以用dp來解決上面的問題。
在這裡我用的是bottom-up[3]的dp思路,也就是從最基本的case開始構造元素,使得在後面的計算裡面可以重複的利用之前的計算。
假設我們開闢乙個2維資料,他的列標誌(column index)表示old裡面的字元,比如說old是fabc,0是空字元'/0',1是'c',2是'b',3是'a',4是'f'。字母順序是反過來的。
而行標誌(row index)表示new裡面的字元,比如說new是ebca,0是空字元'/0',1是'a',2是'c',3是'b',4是'e'。
我們定義oldchars[old.size() + 1]為old裡面的字元, newchars[new.size() + 1]為new裡面的字元,裡面的內容就和上面說的一樣。然後定義table[old.size() + 1][new.size() + 1]是存放lcs的乙個2維陣列。那麼通過上面的lsp公式,我們可以知道兩個字串的lsp是: 1) 如果首個字母是相同的話,也就是oldchars[i] == newchars[j]的時候,那麼lsp就是oldchars[i] + table[i - 1][j - 1]; 2) 如果兩個首字母是不一樣的話,也就是newchars[i] != oldchars[j]的話,那麼lsp就是max(table[i - 1][j - 1], table[i][j - 1])。其中比較的標準是字串較長的為較大的字串,如果長度相等,那麼就是按字典序排序的較大者為較大者。
從而我們可以這樣計算:
function lsp(old, new)
oldsize = old.size()
newsize = new.size()
for i -> 0 to oldsize
for j -> 0 to newsize
if (oldchars[i] == newchars[j])
else
table[i][j] = max(table[i - 1][j], table[i][j - 1])
return table[old.size()][new.size()]
注意,table[i][0]和table[0][j]都被預先賦為空串""了。
通過這樣的計算,最後的table[old.size()][new.size()]就是所求的lcs。
在得到了lsp之後,只要比較old和lsp,不同的就是被刪除了的;比較new和lsp,不同的就是新增加的。
這個dp演算法的時間複雜度是o(nm),空間複雜度是o(nm),不過空間複雜度可以簡化到o(n)。
下面是完整的一段**:
diff演算法 diff演算法介紹
diff演算法的作用 計算出virtual dom中真正變化的部分,並只針對該部分進行原生dom操作,而非重新渲染整個頁面。傳統diff演算法 通過迴圈遞迴對節點進行依次對比,演算法複雜度達到 o n 3 n是樹的節點數,這個有多可怕呢?如果要展示1000個節點,得執行上億次比較。即便是cpu快能執...
diff程式的演算法
diff程式很重要,linux中的源 補丁都是diff作出來的,diff在比較兩個文字檔案的不同方面很高效,它是基於行的,diff會將兩個檔案都按照行分成若干部分,然後計算這些行每一行的校驗碼,之後的問題就是比較這兩個檔案的所有行的校驗碼序列的不同了,這就把問題歸結為了序列比對,diff用的是動態規...
vue的diff演算法
1.當資料發生變化時,vue是怎麼更新節點的?渲染真實dom的開銷很大,比如我們修改了某個資料,如果直接渲染到真實dom會引起整個dom樹的重繪和重排,有沒有可能我們只更新我們修改的那一小塊dom而不更新整個dom呢?diff演算法可以幫助我們。我們根據真實dom生成乙個虛擬dom,當虛擬dom某個...