1. 程式人生 > >資料結構與演算法之美-學習筆記

資料結構與演算法之美-學習筆記

    接上篇文章,在我意識到資料結構與演算法的重要性時,正好在群裡有人分享了極客時間的資料結構與演算法之美的課程,從入門篇、基礎篇、高階篇到實戰篇,由淺入深的講述常用的資料結構與演算法,特別是在留言區作者的留言"邁不過去你找我退錢",我就喜歡這種有自信的人,當然不是完全指望他人幫自己把演算法撿起來,
既然來了,就要全身心的投入,在此立個flag,通過這個階段的學習,理解常用的演算法與資料結構,掌握複雜度的分析,將提高程式碼執行效率思想融入日常開發中。

第1章 為什麼要學習資料結構與演算法
目的:我們學習資料結構與演算法,並不是為了死記硬背幾個知識點。我們的目的是建立時間複雜度、空間複雜度意識,寫出高質量的程式碼,能夠設計基礎架構,提升程式設計技能,訓練邏輯思維,積攢人生經驗,以此獲得工作回報,實現個人價值。
第二章 如何抓住重點,系統高效的學習資料結構與演算法
定義:從廣義上講,資料結構就是指一組資料的儲存結構,演算法就是操作資料的一組方法。
兩者關係:資料結構與演算法是相輔相成的,資料結構是為演算法服務的,演算法要作用在特定的資料結構之上。
                    資料結構是靜態的,它只是組織資料的一種方式。如果不在它的基礎上操作、構建演算法,孤立存在的資料結構就是                        沒用的。
學習重點:效率與資源消耗的度量衡-複雜度分析,10個數據結構:陣列、連結串列、棧、佇列、散列表、二叉樹、堆、跳錶、圖、Trie樹;10個演算法:遞迴、排序、二份查詢、搜尋、雜湊演算法、貪心演算法、分治演算法、回溯演算法、動態規劃、字串匹配演算法。
學習技巧:
                1,邊學邊練,適度刷題。
                2,多問、多思考、多互動。
                3,打怪升級學習法,設立切實可行的目標。
                4,知識需要沉澱,不要試圖一下子掌握所有

第3章 複雜度分析(上):如何分析、統計
複雜度分析的重要性:複雜度分析時整個演算法學習的精髓,只要掌握了它,資料結構與演算法的內容基本上就掌握了一半。
為何需要複雜度分析:
                                        1,測試結果非常依賴測試環境。
                                        2,測試結果受資料規模的影響很大。
大O表示法:T(n)=O(f(n))
                     T(n)表示程式碼執行時間,n表示資料規模的大小,f(n)代表每行程式碼執行的次數總和,O表示程式碼的執行時間T(n)與f(n)成正比。
整個意義:代表程式碼執行時間隨資料規模增長的變化趨勢。
複雜度分析法則:
                    1),單段程式碼看高頻:比如迴圈
                    2),多段程式碼取最大:比如一段程式碼中有單迴圈與多重迴圈,那麼取多重迴圈。
                    3),巢狀程式碼求乘積:比如遞迴、多重迴圈等。
                    4),多個規模求加法:比如方法有兩個引數控制兩個迴圈的次數,那麼這時取二者複雜度相加。
常見的複雜度級別:
多項式階:O(1)常數階,O(logn)對數階,O(n)線性階,O(nlogn)線性對數階,O(n^2)平方階等
非多項式階:隨著資料規模的增長,演算法的執行時間暴增,這類演算法效能極差,包括指數階,階乘階。

第5章 陣列:為什麼很多程式語言中陣列都從0開始編號
    本章開始的時候丟擲了一個問題:為什麼很多程式語言中陣列都是從0開始編號?這個問題很有意思,雖然我經常用到陣列,但是還真的
沒想到過為什麼是從0開始的。
 
  如何實現隨機訪問?
  陣列的定義:陣列是一種線性表資料結構,它用一組連續的記憶體空間,來儲存一組具有相同型別的資料。
  第一是線性表。線性表就是資料排成像一條線一樣的結構。每個線性表上的資料最多隻有前和後兩個方向。除了陣列,連結串列,佇列,棧都是線性結構。
    第二是連續的空間和相同的型別的資料。計算記憶體地址:a[i]_address = base_address + i * data_type_size(陣列和連結串列的區別?)
    陣列刪除(JVM標記清除垃圾回收演算法)。插入刪除的時間複雜度為O(n)
    很多時候,我們並不是要去死記硬背某個資料結構的或者演算法,而是要去學習它背後的思想和處理技巧,這些東西才是最有價值的。 

    如果使用1作為陣列的開始:    
    a[k]_address = base_address + (k-1)*type_size,要多做一次減法運算。

第6章 連結串列(上):如何實現LRU快取淘汰演算法?
連結串列結構:單鏈表、雙向連結串列、迴圈連結串列。
單鏈表:頭結點用來記錄連結串列的基地址,尾節點指向空地址NULL 。
迴圈連結串列:尾節點指向頭結點。著名的約瑟夫問題。
雙向連結串列:具有後繼指標next與前驅指標prev。linkhashpmap用到了雙向列表。

對於執行較慢的程式,可以通過消耗更多的記憶體(空間換時間,比如快取)來進行優化;
而消耗過多記憶體的程式,可以通過消耗更多的時間(時間換空間,比如手機微控制器)來降低記憶體的消耗。

連結串列 & 陣列
陣列:插入刪除O(n),隨機訪問O(1). 缺點:大小固定,需要連續的內容空間。如果太小,如要擴容,則輸入需要遷移。如果太大,則容易導致記憶體不足,使用效率不高。
連結串列:插入刪除O(1),隨機訪問O(n). 缺點:儲存同樣大小資料,需要更多空間,因為節點。連結串列的頻繁插入刪除容易導致記憶體碎片,造成GC .

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

ArrayList 詳解

ArrayList是一個數組佇列,相當於動態陣列。ArrayList的操作不是執行緒安全的。

ArrayList包含了兩個重要的物件:elementData 和 size。

(01) elementData 是"Object[]型別的陣列",它儲存了新增到ArrayList中的元素。實際上,elementData是個動態陣列,我們能通過建構函式 ArrayList(int initialCapacity)來執行它的初始容量為initialCapacity;如果通過不含引數的建構函式ArrayList()來建立ArrayList,則elementData的容量預設是10。elementData陣列的大小會根據ArrayList容量的增長而動態的增長,具體的增長方式,請參考原始碼分析中的ensureCapacity()函式。

(02) size 則是動態陣列的實際大小。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------