本書看完,對常見的數據結構與算法從概念上有了更深刻的理解。算法
書中關於數組、棧和隊列、鏈表、字典、散列、集合、二叉樹、圖、排序、檢索、動態規劃、貪心算法都有詳細的介紹。算是一本不錯的學習書籍。數組
棧和隊列:均可以經過數組來模擬。棧的規則是先進後出,隊列的規則是先進先出。數據結構
鏈表:有雙向與單向鏈表之分,默認有一個頭指針,指向下一個節點,就像鏈條同樣。函數
數組 vs 鏈表:性能
字典:簡單理解就是key/value的鍵值對存儲的對象。學習
散列:基於數組設計,長度預約。藉助散列函數將每一個鍵值映射爲一個惟一的數組索引。優化
集合:js中的map、set。設計
二叉樹:非線性的數據結構,以分層的方式存儲。用來存儲具備層級關係的數據。好比文件系統。具備根節點、左子樹、右子樹、葉子節點。樹的層數就是樹的深度。樹的遍歷:指針
圖:由邊的集合及頂點的集合組成。地圖就是一種圖。若是一個圖的頂點是有序的,則稱之爲有向圖,反之爲無向圖。code
常見的排序:冒泡排序、選擇排序、插入排序
高級排序:希爾排序、歸併排序。
檢索:二分查找算法,比排序查找算法高效。
使用遞歸方案能解決的問題,都可以使用動態規劃技巧來解決。
動態規劃:解決類似子問題,並保存子問題的解,經過子問題的解從而解決整個問題。子問題的解一般存儲在數組中便於訪問
動態規劃,聽過它的鼎鼎大名,但一直沒深刻了解過。
書中的例子:
1.計算裴波那契數列
斐波那契數列:0,1,1,2,3,5,8,13...
該序列是由前兩項數值相加而成的
遞歸實現:效率低,有太多值在遞歸調用中被從新計算。
function recurFib (n) { if (n < 2) { return n; } return recurFib(n - 1) + recurFib(n - 2); }
動態規劃實現:將數組的每一個元素賦值爲前兩個元素之和,循環結束,最後一個元素值即爲最終計算獲得的斐波那契數值
function dynFib(n) { var val = []; for (var i = 0; i <= n; i++) { // 0 - n:含 n+1 個 val[i] = 0; } if (n == 1 || n == 2) { return 1; } val[1] = 1; val[2] = 2; for (var i = 3; i <= n; i++) { val[3] = val[i-1] + val[i-2]; } return val[n-1]; }
迭代實現:不保存中間結果
function iterFib (n) { var last = 1; var nextLast = 1; var result = 1; for (var i = 2; i < n; i++) { result = last + nextLast; nextLast = last; last = result; } return result; }
2.尋找兩個字符串的最長公共子串
動態規劃:使用一個二維數組存儲兩個字符串相同位置的字符比較結果。初始數組每個元素被設置爲0,每次在這兩個數組的相同位置發現了匹配,就將該數組對應行和列的元素加1,不然保持爲0
function lcs (word1, word2) { // 初始化二維數組,每項爲0 var max = 0; var index = 0; var lcsarr = new Array(word1.length + 1); for (var i = 0; i <= word1.length + 1; i++) { lcsarr[i] = new Array(word2.length + 1); for (var j = 0; j <= word2.length + 1; j++) { lcsarr[i][j] = 0; } } // 保存字符匹配的記錄,若是兩個字符串相應位置的字符進行了匹配,當前數組元素的值將被設置爲前一次循環中數組元素保存的值加1,最後若是變量max的值比如今存儲在數組中的當前元素小,max的值將被賦值給這個元素,變量index的值將被設置爲i的當前值。這兩個變量將在函數的最後一部分用於肯定從哪裏開始獲取最長公共子串。 for (var i = 0; i <= word1.length; i++) { for (var j = 0; j <= word2.length; j++) { if (i == 0 || j == 0) { lcsarr[i][j] = 0; } else { if (word1[i - 1] == word2[j - 1]) { lcsarr[i][j] = lcsarr[i - 1][j - 1] + 1; } else { lcsarr[i][j] = 0; } } if (max < lcsarr[i][j]) { max = lcsarr[i][j]; index = i; } } } // 確認從哪裏開始構建這個最長公共子串。以變量index減去變量max的差值做爲起始點,以變量max的值做爲終點。 var str = ''; if (max == 0) { return ''; } else { for (var i = index - max; i <= max; i++) { str += word2[i]; } return str; } } console.log(lcs('raven', 'havoc')); // av
關於第二部分,記錄字符串匹配結果,能夠優化,減小遍歷次數
for (var i = 1; i <= word1.length; i++) { for (var j = 1; j <= word2.length; j++) { if (word1[i - 1] == word2[j - 1]) { lcsarr[i][j] = lcsarr[i - 1][j - 1] + 1; } if (max < lcsarr[i][j]) { max = lcsarr[i][j]; index = i; } } }
書中還有關於如何用遞歸與動態規劃解決揹包問題,甚至用貪心算法解決部分揹包問題。
揹包問題:有一個保險箱,保險箱中的物品規格和價值不一樣。你須要將保險箱中的寶物放入一個你的揹包,但願揹包裝進的寶貝價值最大。如:保險箱中有5件物品,尺寸:3,4,7,8,9,價值:4,5,10,11,13,揹包的容積爲16。最優解是:選取第三件和第五件,總尺寸是16,總價值是23。
若是感興趣的,能夠翻開此書看看。