快速入門資料結構和演算法

阿里技術發表於2020-08-12

快速入門資料結構和演算法

阿里妹導讀:有哪些常見的資料結構?基本操作是什麼?常見的排序演算法是如何實現的?各有什麼優缺點?本文簡要分享演算法基礎、常見的資料結構以及排序演算法,給同學們帶來一堂資料結構和演算法的基礎課。

一  前言

1  為什麼要學習演算法和資料結構?

  • 解決特定問題。

  • 深度最佳化程式效能的基礎。

  • 學習一種思想:如何把現實問題轉化為計算機語言表示。

2  業務開發要掌握到程度?

  • 瞭解常見資料結構和演算法,溝通沒有障礙。

  • 活學活用:遇到問題時知道要用什麼資料結構和演算法去最佳化。

二  資料結構基礎

1  什麼是資料結構?

資料結構是資料的組織、管理和儲存格式,其使用目的是為了高效的訪問和修改資料。

資料結構是演算法的基石。如果把演算法比喻成美麗靈動的舞者,那麼資料結構就是舞者腳下廣闊而堅實的舞臺。

2  物理結構和邏輯結構的區別?

物理結構就像人的血肉和骨骼,看得見,摸得著,實實在在,如陣列、連結串列。

邏輯結構就像人的思想和精神,它們看不見、摸不著,如佇列、棧、樹、圖。

3  線性儲存結構和非線性儲存結構的區別?

  • 線性:元素之間的關係是一對一的,如棧、佇列。

  • 非線性:每個元素可能連線0或多個元素,如樹、圖。

三  演算法基礎

1  什麼是演算法?

  • 數學:演算法是用於解決某一類問題的公式和思想。

  • 計算機:一系列程式指令,用於解決特定的運算和邏輯問題。

2  如何衡量演算法好壞?

  • 時間複雜度:執行時間長短。

  • 空間複雜度:佔用記憶體大小。

3  怎麼計算時間複雜度?

大O表示法(漸進時間複雜度):把程式的相對執行時間函式T(n)簡化為一個數量級,這個數量級可以是n、n^2、logN等。

推導時間複雜度的幾個原則:

  • 如果執行時間是常數量級,則用常數1表示。

  • 只保留時間函式中的最高階項。

  • 如果最高階項存在,則省去最高項前面的係數。

時間複雜度對比:O(1) > O(logn) > O(n) > O(nlogn) > O(n^2)。

不同時間複雜度演算法執行次數對比:

快速入門資料結構和演算法

4  怎麼計算空間複雜度?

常量空間 O(1):儲存空間大小固定,和輸入規模沒有直接的關係。

線性空間 O(n):分配的空間是一個線性的集合,並且集合大小和輸入規模n成正比。

二維空間 O(n^2):分配的空間是一個二維陣列集合,並且集合的長度和寬度都與輸入規模n成正比。

遞迴空間 O(logn):遞迴是一個比較特殊的場景。雖然遞迴程式碼中並沒有顯式的宣告變數或集合,但是計算機在執行程式時,會專門分配一塊記憶體空間,用來儲存“方法呼叫棧”。執行遞迴操作所需要的記憶體空間和遞迴的深度成正比。

5  如何定義演算法穩定性?

穩定:如果a原本在b前面,而a=b,排序之後a仍然在b的前面。

不穩定:如果a原本在b的前面,而a=b,排序之後 a 可能會出現在 b 的後面。

6  有哪些常見演算法?

首先要明確:特定演算法解決特定問題。

  • 字串:暴力匹配、BM、KMP、Trie等。

  • 查詢:二分查詢、遍歷查詢等。

  • 排序:氣泡排序、快排、計數排序、堆排序等。

  • 搜尋:TFIDF、PageRank等。

  • 聚類分析:期望最大化、k-meanings、k-數位等。

  • 深度學習:深度信念網路、深度卷積神經網路、生成式對抗等。

  • 異常檢測:k最近鄰、區域性異常因子等。

  • ......

其中,字串、查詢、排序演算法是最基礎的演算法。


四  常見資料結構

1  陣列

1)什麼是陣列?

資料是有限個相同型別的變數所組成的有序集合。陣列中的每一個變數被稱為元素。

快速入門資料結構和演算法


2)陣列的基本操作?

讀取O(1)、更新O(1)、插入O(n)、刪除O(n)、擴容O(n)。

2  連結串列

1)什麼是連結串列?

連結串列是一種在物理上非連續、非順序的資料結構,由若干個節點組成。

單向連結串列的每一個節點又包含兩部分,一部分是存放資料的變數data,另一部分是指向下一個節點的指標next。

快速入門資料結構和演算法

2)連結串列的基本操作?

讀取O(n)、更新O(1)、插入O(1)、刪除O(1)。

3)連結串列 VS 陣列

陣列:適合多讀、插入刪除少的場景。

連結串列:適用於插入刪除多、讀少的場景。

快速入門資料結構和演算法

3  棧

1)什麼是棧?

棧是一種線性邏輯資料結構,棧的元素只能後進先出。最早進入的元素存放的位置叫做棧底,最後進入的元素存放的位置叫棧頂。

一個比喻,棧是一個一端封閉一端的開放的中空管子,佇列是兩端開放的中空管子。

快速入門資料結構和演算法

2)如何實現棧?

陣列實現:

快速入門資料結構和演算法

連結串列實現:

快速入門資料結構和演算法

3)棧的基本操作

入棧O(1)、出棧O(1)。

4)棧的應用?

  • 回溯歷史,比如方法呼叫棧。

  • 頁面麵包屑導航。

4  佇列

1)什麼是佇列?

一種線性邏輯資料結構,佇列的元素只能後進後出。佇列的出口端叫做隊頭,佇列的入口端叫做隊尾。

快速入門資料結構和演算法

2)如何實現佇列?

陣列實現:

快速入門資料結構和演算法

連結串列實現:

快速入門資料結構和演算法

3)佇列的基本操作?

入隊 O(1)、出隊 O(1)。

4)佇列的應用

  • 訊息佇列

  • 多執行緒的等待佇列

  • 網路爬蟲的待爬URL佇列

5  雜湊表

1)什麼是雜湊表?

一種邏輯資料結構,提供了鍵(key)和值(value)的對映關係。


快速入門資料結構和演算法


2)雜湊表的基本操作?

寫入:O(1)、讀取:O(1)、擴容O(n)。

3)什麼是雜湊函式?

雜湊表本質上是一個陣列,只是陣列只能根據下標,像a[0] a[1] a[2] a[3] 這樣來訪問,而雜湊表的key則是以字串型別為主的。

透過雜湊函式,我們可以把字串或其他型別的key,轉化成陣列的下標index。

如給出一個長度為8的陣列,則:

當key=001121時,

index = HashCode ("001121") % Array.length = 7

當key=this時,

index = HashCode ("this") % Array.length = 6

快速入門資料結構和演算法

4)什麼是雜湊衝突?

不同的key透過雜湊函式獲得的下標有可能是相同的,例如002936這個key對應的陣列下標是2,002947對應的陣列下標也是2,這種情況就是雜湊衝突。

快速入門資料結構和演算法

5)如何解決雜湊衝突?

開放定址法:例子Threadlocal。

快速入門資料結構和演算法

連結串列法:例子Hashmap。

快速入門資料結構和演算法

6  樹

1)什麼是樹?

樹(tree)是n(n≥0)個節點的有限集。

當n=0時,稱為空樹。在任意一個非空樹中,有如下特點:

  • 有且僅有一個特定的稱為根的節點。

  • 當n>1時,其餘節點可分為m(m>0)個互不相交的有限集,每一個集合本身又是一個樹,並稱為根的子樹。

2)樹的遍歷?

(1)深度優先

前序:根節點、左子樹、右子樹。

快速入門資料結構和演算法

中序:左子樹、根節點、右子樹。

快速入門資料結構和演算法

後序:左子樹、右子樹、根節點。

快速入門資料結構和演算法

實現方式:遞迴或棧。

(2)廣度優先

層序:一層一層遍歷。

快速入門資料結構和演算法


實現方式:佇列。

7  二叉樹

1)什麼是二叉樹?

二叉樹(binary tree)是樹的一種特殊形式。二叉,顧名思義,這種樹的每個節點最多有2個孩子節點。注意,這裡是最多有2個,也可能只有1個,或者沒有孩子節點。

2)什麼是滿二叉樹?

一個二叉樹的所有非葉子節點都存在左右孩子,並且所有葉子節點都在同一層級上,那麼這個樹就是滿二叉樹。

3)什麼是完全二叉樹?

對一個有n個節點的二叉樹,按層級順序編號,則所有節點的編號為從1到n。如果這個樹所有節點和同樣深度的滿二叉樹的編號為從1到n的節點位置相同,則這個二叉樹為完全二叉樹。


快速入門資料結構和演算法

8  二叉查詢樹

1)什麼是二叉查詢樹?

二叉查詢樹在二叉樹的基礎上增加了以下幾個條件:

  • 如果左子樹不為空,則左子樹上所有節點的值均小於根節點的值。

  • 如果右子樹不為空,則右子樹上所有節點的值均大於根節點的值。

  • 左、右子樹也都是二叉查詢樹。

快速入門資料結構和演算法

2)二叉查詢樹的作用?

  • 查詢==》二分查詢。

  • 排序==》中序遍歷。

3)二叉樹的實現方式?

  • 連結串列。

  • 陣列:對於稀疏二叉樹來說,陣列表示法是非常浪費空間的。

9  二叉堆

1)什麼是二叉堆?

二叉堆是一種特殊的完全二叉樹,它分為兩個型別:最大堆和最小堆。

  • 最大堆任何一個父節點的值,都大於或等於它左、右孩子節點的值。

  • 最小堆的任何一個節點的值,都小於或等於它左、右孩子節點的值。

快速入門資料結構和演算法

2)二叉堆的基本操作?

(1)插入:插入最末,節點上浮。

快速入門資料結構和演算法

(2)刪除:刪除頭節點,尾節點放到頭部,再下沉。

快速入門資料結構和演算法

(3)構建二叉堆:二叉樹==》二叉堆,所有非葉子節點依次下沉。

快速入門資料結構和演算法

3)二叉堆的實現方式?

陣列:

快速入門資料結構和演算法

五  常見排序演算法

1  十大經典排序演算法

快速入門資料結構和演算法

2  氣泡排序

1)演算法描述

氣泡排序是一種簡單的排序演算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果它們的順序錯誤就把它們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個演算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。 

2)實現步驟

快速入門資料結構和演算法

快速入門資料結構和演算法

  • 比較相鄰的元素。如果第一個比第二個大,就交換它們兩個。

  • 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對,這樣在最後的元素應該會是最大的數。

  • 針對所有的元素重複以上的步驟,除了最後一個。

  • 重複步驟1~3,直到排序完成。

3)優缺點

  • 優點:實現和理解簡單。

  • 缺點:時間複雜度是O(n^2),排序元素多時效率比較低。

4)適用範圍

資料已經基本有序,且資料量較小的場景。

5)場景最佳化

(1)已經有序了還再繼續冒泡問題

  • 本輪排序中,元素沒有交換,則isSorted為true,直接跳出大迴圈,避免後續無意義的重複。

(2)部分已經有序了,下一輪的時候但還是會被遍歷

  • 記錄有序和無序資料的邊界,有序的部分在下一輪就不用遍歷了。

(3)只有一個元素不對,但需要走完全部輪排序

  • 雞尾酒排序:元素的比較和交換是雙向的,就像搖晃雞尾酒一樣。

3  歸併排序

1)演算法描述

歸併排序是建立在歸併操作上的一種有效的排序演算法。該演算法是採用分治法的一個非常典型的應用。遞迴的把當前序列分割成兩半(分割),在保持元素順序的同時將上一步得到的子序列整合到一起(歸併),最終形成一個有序數列。

2)實現步驟

快速入門資料結構和演算法

圖源:https://www.cnblogs.com/chengxiao/p/6194356.html

  • 把長度為n的輸入序列分成兩個長度為n/2的子序列。

  • 對這兩個子序列分別採用歸併排序。

  • 將兩個排序好的子序列合併成一個最終的排序序列。

3)優缺點

優點:

  • 效能好且穩定,時間複雜度為O(nlogn) 。

  • 穩定排,適用場景更多。

缺點:

  • 非原地排序,空間複雜度高。

4)適用範圍

大資料量且期望要求排序穩定的場景。

4  快速排序

1)演算法描述

快速排序使用分治法策略來把一個序列分為較小和較大的2個子序列,然後遞迴地排序兩個子序列,以達到整個數列最終有序。

2)實現步驟

快速入門資料結構和演算法


快速入門資料結構和演算法

  • 從數列中挑出一個元素,稱為 “基準值”(pivot)。

  • 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分割槽退出之後,該基準就處於數列的中間位置。這個稱為分割槽(partition)操作。

  • 遞迴地對【小於基準值元素的子數列】和【大於基準值元素的子數列】進行排序。

3)優缺點

優點:

  • 效能較好,時間複雜度最好為O(nlogn),大多數場景效能都接近最優。

  • 原地排序,時間複雜度優於歸併排序。

缺點:

  • 部分場景,排序效能最差為O(n^2)。

  • 不穩定排序。

4)適用範圍

大資料量且不要求排序穩定的場景。

5)場景最佳化

(1)每次的基準元素都選中最大或最小元素

  • 隨機選擇基準元素,而不是選擇第一個元素。

  • 三數取中法,隨機選擇三個數,取中間數為基準元素。

(2)數列含有大量重複資料

  • 大於、小於、等於基準值。

(3)快排的效能最佳化

  • 雙軸快排:2個基準數,例子:Arrays.sort() 。

5  堆排序

1)演算法描

堆排序(Heapsort)是指利用堆這種資料結構所設計的一種排序演算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。

2)實現步驟

快速入門資料結構和演算法


  • 將初始待排序關鍵字序列(R1,R2….Rn)構建成最大堆,此堆為初始的無序區。

  • 將堆頂元素R[1]與最後一個元素R[n]交換,此時得到新的無序區(R1,R2,……Rn-1)和新的有序區(Rn),且滿足R[1,2…n-1]<=R[n]。

  • 由於交換後新的堆頂R[1]可能違反堆的性質,因此需要對當前無序區(R1,R2,……Rn-1)調整為新堆,然後再次將R[1]與無序區最後一個元素交換,得到新的無序區(R1,R2….Rn-2)和新的有序區(Rn-1,Rn)。不斷重複此過程直到有序區的元素個數為n-1,則整個排序過程完成。

3)優缺點

優點:

  • 效能較好,時間複雜度為O(nlogn)。

  • 時間複雜度比較穩定。

  • 輔助空間複雜度為O(1)。

缺點:

  • 資料變動的情況下,堆的維護成本較高。

4)適用範圍

資料量大且資料呈流式輸入的場景。

5)為什麼實際情況快排比堆排快?

堆排序的過程可知,建立最大堆後,會將堆頂的元素和最後一個元素對調,然後讓那最後一個元素從頂上往下沉到恰當的位置,因為底部的元素一定是比較小的,下沉的過程中會進行大量的近乎無效的比較。所以堆排雖然和快排一樣複雜度都是O(NlogN),但堆排複雜度的常係數更大。

6  計數排序

1)演算法描述

計數排序不是基於比較的排序演算法,其核心在於將輸入的資料值轉化為鍵儲存在額外開闢的陣列空間中。作為一種線性時間複雜度的排序,計數排序要求輸入的資料必須是有確定範圍的整數。

2)實現步驟

快速入門資料結構和演算法

  • 找出待排序的陣列中最大元素。

  • 構建一個陣列C,長度為最大元素值+1。

  • 遍歷無序的隨機數列,每一個整數按照其值對號入座,對應陣列下標的值加1。

  • 遍歷陣列C,輸出陣列元素的下標值,元素的值是幾就輸出幾次。

3)優缺點

優點:

  • 效能完爆比較排序,時間複雜度為O(n+k),k為數列最大值。

  • 穩定排序。

缺點:

  • 適用範圍比較狹窄。

4)適用範圍

數列元素是整數,當k不是很大且序列比較集中時適用。

5)場景最佳化

(1)數字不是從0開始,會存在空間浪費的問題

  • 數列的最小值作為偏移量,以數列最大值-最小值+1作為統計陣列的長度。

7  桶排序

1)演算法描述

桶排序是計數排序的升級版。它利用了函式的對映關係,高效與否的關鍵就在於這個對映函式的確定。實現原理:假設輸入資料服從均勻分佈,將資料分到有限數量的桶裡,每個桶再分別排序(有可能再使用別的排序演算法或是以遞迴方式繼續使用桶排序進行排序)。

2)實現步驟

快速入門資料結構和演算法

  • 建立桶,區間跨度=(最大值-最小值)/(桶的數量-1)。

  • 遍歷數列,對入座。

  • 每個桶內進行排序可選擇快排等。

  • 遍歷所有的桶,輸出所有元素。

3)優缺點

優點:

  • 最優時間複雜度為O(n),完爆比較排序演算法。

缺點:

  • 適用範圍比較狹窄。

  • 時間複雜度不穩定。

4)適用範圍

資料服從均勻分佈的場景。

8  效能對比

隨機生成區間0 ~ K之間的序列,共計N個數字,利用各種演算法進行排序,記錄排序所需時間。

快速入門資料結構和演算法

參考內容及圖源

[1]《漫畫演算法:小灰的演算法之旅》

[2]《演算法(第4版)》

[3]《演算法圖解》

[4]《劍指Offer》

[5]十大經典排序演算法(動圖演示)
https://www.cnblogs.com/onepixel/p/7674659.html

[6]維基百科
https://zh.wikipedia.org/wiki/Wikipedia:%E9%A6%96%E9%A1%B5

相關文章