Diff演算法研究

2021-05-23 15:18:00 字數 2659 閱讀 4202

在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某個...