#####廢話的開始#####,從今天開始,可能就會多一個話題了,雖然以前在學校也學過資料結構這門課程,但是作為新世紀的90後,還是把學到的東西原原本本還給了老師,有借有還再借不難。其實後續也陸陸續續學習過,但是沒有去做很好總結,容易忘記,畢竟作為一個半路出家的碼農來說,這個需要好好學習,主要是沒怎麼寫程式碼,所以基本沒用到這些,就算寫了程式碼,好像也用的不多,除非是從事底層開發的小夥伴,不過學會了這個資料結構和演算法還是有有很多好處的,具體怎樣,請繼續往下看。
為什麼要寫一個資料結構和演算法的相關的介紹呢?學這個前至少也要知道自己為什麼要學這個東西吧,學好了可以幹嘛,可以怎麼去學習。此文只是為了更好的從全域性去了解資料結構和演算法,為後期的學習做一個認知,做一個全域性的認知後,對後期的學習會更好,會更加的系統,更加的鞏固,對於資料結構和演算法的介紹所以也是很重要的,因為我們每做一件事情之前都要知道做這件事的目的,能夠帶來什麼好處,怎麼去做,用什麼方式做的更好更有效率。不說別的吧,就說對於面試就很有用,看java的原始碼也有用,越是接觸到底層的程式碼,就越能體現資料結構和演算法的重要性。如果你只是滿足於呼叫封裝好的程式碼,那就到此為止,沒必要往下看,如果想好好的瞭解資料結構和演算法,就認真的往下看,什麼是資料結構和演算法,有什麼用,有什麼魅力。
一、為什麼要學資料結構和演算法?
其實只要作為一個碼程式碼的程式設計師來說,寫程式的都需要學這個,畢竟:程式 = 資料結構 + 演算法 + 程式設計語言
資料結構和演算法是程式的靈魂啊,失去靈魂的程式肯定是沒精神氣的(臃腫,速度慢,消耗資源多)
裝逼!強行裝逼!!強行一起吹水!!!這點很重要,其實下面的才是重點:
- 演算法鍛鍊自己的邏輯思維;
- 提升程式碼效能,節省空間複雜度和時間複雜度
- 能夠在學習的過程中看的懂原始碼,能夠在什麼場景下知道選用更有的演算法方案
- 找工作面試,有的甚至要現場寫程式碼,升職加薪
- 一流的程式設計師搞演算法,二流的程式設計師搞架構,三流的程式設計師搞業務;
- 很多時候根據公司的場景可以有選擇性的時間換空間或者空間換時間,這樣就增加了選擇
- 更好的理解應用軟體和框架,很多知名軟體和框架中都大量用了資料結構演算法,比如mysql的索引用了b+樹,redis的list底層用了跳躍表,理解這些資料結構能更好的幫助我們理解使用這些軟體
總之,很重要!很重要!!真的很重要!!!
二、資料結構和演算法是什麼?
2.1、資料結構介紹
資料結構就是把資料組織起來,為了更方便地使用資料我們為了解決問題,需要將資料儲存下來,然後根據資料的儲存方式來設計演算法實現進行處理,那麼資料的儲存方式不同就會導致需要不同的演算法進行處理。我們希望演算法解決問題的效率越快越好,於是我們就需要考慮資料究竟如何儲存的問題,這就是資料結構。
資料結構可分為:線性結構和非線性結構。
線性結構:
- 線性結構作為最常用的資料結構,其特點是資料元素之間存在一對一的線性關係
- 線性結構有兩種不同的儲存結構,即順序儲存結構(陣列)和鏈式儲存結構(連結串列)。順序儲存的線性表稱為順序表,順序表中的儲存元素是連續的
- 鏈式儲存的線性表稱為連結串列,連結串列中的儲存元素不一定是連續的,元素節點中存放資料元素以及相鄰元素的地址資訊
- 線性結構常見的有:陣列、佇列、連結串列和棧.
非線性結構:
非線性結構包括:二維陣列,多維陣列,廣義表,樹結構,圖結構
非線性結構可以繼續分:集合、樹形結構、圖狀結構
- 集合結構:除了同屬於一種型別外,別無其它關係。
- 樹形結構:元素之間存在一對多關係。常見型別有:樹(有許多特例:二叉樹、平衡二叉樹、查詢樹等)
- 圖形結構:元素之間存在多對多關係,圖形結構中每個結點的前驅結點數和後續結點多個數可以任意。
儲存結構表示資料在計算機中的表現形式:
- 順序儲存結構:順序儲存結構將資料儲存在地址連續的儲存單元裡。
- 連結儲存結構:鏈式儲存結構將資料儲存在任意的儲存單元裡,通過儲存地址的方式找到相關聯的資料元素。
- 索引儲存結構;在儲存資料的同時,簡歷資料的索引資料,方便對資料進行查詢;
- 雜湊儲存結構:通過雜湊函式對關鍵字進行計算算出元素的儲存地址
2.2、演算法介紹
演算法(Algorithm)是指解題方案的準確而完整的描述,是一系列解決問題的清晰指令,演算法代表著用系統的方法描述解決問題的策略機制。也就是說,能夠對一定規範的輸入,在有限時間內獲得所要求的輸出。如果一個演算法有缺陷,或不適合於某個問題,執行這個演算法將不會解決這個問題。不同的演算法可能用不同的時間、空間或效率來完成同樣的任務。一個演算法的優劣可以用空間複雜度與時間複雜度來衡量。----演算法百度百科
演算法是計算機處理資訊的本質,因為計算機程式本質上是一個演算法來告訴計算機確切的步驟來執行一個指定的任務。一般地,當演算法在處理資訊時,會從輸入裝置或資料的儲存地址讀取資料,把結果寫入輸出裝置或某個儲存地址供以後再呼叫。演算法是獨立存在的一種解決問題的方法和思想。
其實演算法就是解決問題的一種方法或者是思路,所以每個演算法都有自己的應用場景,所以很多情況要根據實際情況選擇合適的演算法或者是設計演算法。所以明白演算法的原理,至少能夠讓你在面對不同的場景的時候能夠正確的選擇哪一種演算法,提高效率,節約資源,這都是時間和白花花的銀子啊,所以好好學習演算法很重要。
演算法的五大特性:
- 有窮性(Finiteness):演算法的有窮性是指演算法必須能在執行有限個步驟之後終止;
- 確切性(Definiteness):演算法的每一步驟必須有確切的定義;
- 輸入項(Input):一個演算法有0個或多個輸入,以刻畫運算物件的初始情況,所謂0個輸入是指演算法本身定出了初始條件;
- 輸出項(Output):一個演算法有一個或多個輸出,以反映對輸入資料加工後的結果。沒有輸出的演算法是毫無意義的;
- 可行性(Effectiveness):演算法中執行的任何計算步驟都是可以被分解為基本的可執行的操作步驟,即每個計算步驟都可以在有限時間內完成(也稱之為有效性)
時間複雜度:
演算法的時間複雜度是指執行演算法所需要的計算工作量。一般來說,計算機演算法是問題規模n 的函式f(n),演算法的時間複雜度也因此記做。T(n)=Ο(f(n)),因此,問題的規模n 越大,演算法執行的時間的增長率與f(n) 的增長率正相關,稱作漸進時間複雜度(Asymptotic Time Complexity)。
- 時間頻度 一個演算法執行所耗費的時間,從理論上是不能算出來的,必須上機執行測試才能知道。但我們不可能也沒有必要對每個演算法都上機測試,只需知道哪個演算法花費的時間多,哪個演算法花費的時間少就可以了。並且一個演算法花費的時間與演算法中語句的執行次數成正比例,哪個演算法中語句執行次數多,它花費時間就多。一個演算法中的語句執行次數稱為語句頻度或時間頻度。記為T(n)。
- 時間複雜度 在剛才提到的時間頻度中,n稱為問題的規模,當n不斷變化時,時間頻度T(n)也會不斷變化。但有時我們想知道它變化時呈現什麼規律。為此,我們引入時間複雜度概念。 一般情況下,演算法中基本操作重複執行的次數是問題規模n的某個函式,用T(n)表示,若有某個輔助函式f(n),使得當n趨近於無窮大時,T(n)/f(n)的極限值為不等於零的常數,則稱f(n)是T(n)的同數量級函式。記作T(n)=O(f(n)),稱O(f(n)) 為演算法的漸進時間複雜度,簡稱時間複雜度。
- 在各種不同演算法中,若演算法中語句執行次數為一個常數,則時間複雜度為O(1),另外,在時間頻度不相同時,時間複雜度有可能相同,如T(n)=n^2+3n+4與T(n)=4n^2+2n+1它們的頻度不同,但時間複雜度相同,都為O(n^2)。 按數量級遞增排列,常見的時間複雜度有:常數階O(1),對數階O(log2n),線性階O(n), 線性對數階O(nlog2n),平方階O(n^2),立方階O(n^3),..., k次方階O(n^k),指數階O(2^n)。隨著問題規模n的不斷增大,上述時間複雜度不斷增大,演算法的執行效率越低。
常見的時間複雜度:
執行次數函式舉例 | 階 | 非正式術語 |
12 | O(1) | 常數階 |
2n+3 | O(n) | 線性階 |
3n^2+2n+1 | O(n^2) | 平方階 |
5log2n+20 | O(logn) | 對數階 |
2n+3nlog2n+19 | O(nlogn) | nlogn階 |
6n^3+2n^2+3n+4 | O(n^3) | 立方階 |
2^n | O(2^n) | 指數階 |
常見時間複雜度之間的關係,所消耗的時間從小到達:O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
空間複雜度:
演算法的空間複雜度是指演算法需要消耗的記憶體空間。其計算和表示方法與時間複雜度類似,一般都用複雜度的漸近性來表示。同時間複雜度相比,空間複雜度的分析要簡單得多.
演算法的空間複雜度(Space Complexity)S(n)定義為該演算法所耗費的儲存空間,它也是問題規模n的函式。漸近空間複雜度也常常簡稱為空間複雜度。記為S(n)=O(f(n))
空間複雜度(Space Complexity)是對一個演算法在執行過程中臨時佔用儲存空間大小的量度。一個演算法在計算機儲存器上所佔用的儲存空間,包括儲存演算法本身所佔用的儲存空間,演算法的輸入輸出資料所佔用的儲存空間和演算法在執行過程中臨時佔用的儲存空間這三個方面。演算法的輸入輸出資料所佔用的儲存空間是由要解決的問題決定的,是通過參數列由呼叫函式傳遞而來的,它不隨本演算法的不同而改變。儲存演算法本身所佔用的儲存空間與演算法書寫的長短成正比,要壓縮這方面的儲存空間,就必須編寫出較短的演算法。演算法在執行過程中臨時佔用的儲存空間隨演算法的不同而異,有的演算法只需要佔用少量的臨時工作單元,而且不隨問題規模的大小而改變,我們稱這種演算法是“就地\"進行的,是節省儲存的演算法,有的演算法需要佔用的臨時工作單元數與解決問題的規模n有關,它隨著n的增大而增大,當n較大時,將佔用較多的儲存單元。
當然評價演算法的好壞不僅僅是演算法的時間複雜度和空間複雜度,比如還有正確性,可讀性,鍵值性等,明白了時間複雜度和空間複雜度,就知道什麼情況下什麼場景可以考慮時間換空間,或者空間換時間了,這些都是根據實際情況而定的,沒有最好的演算法,只有更適合的某些場景的演算法。
演算法中常用的分析方法:遞推法、遞迴法、窮舉法、貪心演算法、分治法、動態規劃法、迭代法、分支界限法、回溯法。
演算法的分類:
演算法可大致分為基本演算法、資料結構的演算法、數論與代數演算法、計算幾何的演算法、圖論的演算法、動態規劃以及數值分析、加密演算法、排序演算法、檢索演算法、隨機化演算法、並行演算法,厄米變形模型,隨機森林演算法。演算法可以巨集泛的分為三類:
- 有限的,確定性演算法 這類演算法在有限的一段時間內終止。他們可能要花很長時間來執行指定的任務,但仍將在一定的時間內終止。這類演算法得出的結果常取決於輸入值。
- 有限的,非確定演算法 這類演算法在有限的時間內終止。然而,對於一個(或一些)給定的數值,演算法的結果並不是唯一的或確定的。
- 無限的演算法 是那些由於沒有定義終止定義條件,或定義的條件無法由輸入的資料滿足而不終止執行的演算法。通常,無限演算法的產生是由於未能確定的定義終止條件 。
三、如何學習資料結構和演算法?
- 明確目的,為啥要學習資料結構和演算法--提供動力
- 明確方向,明白自己要學習資料結構和演算法中的哪些內容
- 動手,動手,動手,懂原理,程式碼實現,還有最重要的一步就是總結和分享
- 明白每個資料結構和演算法的來歷,特點,優缺點和使用場景
- 可以去看一些java中的原始碼,想想別人優秀程式碼的實現,為什麼要這麼寫,還有更優的方案嗎?
- 當然資源也很重要:可以聽課,網上課程很多,看書籍《演算法導論》《數學之美》等,網路資源:演算法視覺化網站(中文版)
- 可以適度的刷題,這樣可以強化記憶
- 學會提出問題,解決問題,記錄問題,實現解決方案
比如常見的一些資料結構和演算法:
- 10 個資料結構:陣列、連結串列、棧、佇列、雜湊表、二叉樹、堆、跳錶、圖、Trie 樹;
- 10 個演算法:遞迴、排序、二分查詢、搜尋、雜湊演算法、貪心演算法、分治演算法、回溯演算法、動態規劃、字串匹配演算法
資料結構和演算法知識結構思維導圖(引用優秀博文,見參考文章):
參考:
https://www.cnblogs.com/54chensongxia/p/11448695.html
https://www.cnblogs.com/chjxbt/p/10967968.html
https://baike.baidu.com/item/%E7%AE%97%E6%B3%95/209025
https://www.cnblogs.com/songQQ/archive/2009/10/20/1587122.html