數據結構與算法之美學習筆記(1~4章)

第一章.爲什麼要學習數據結構和算法

 


 

第二章.數據結構和算法的定義,學習的方法

1.數據結構和算法的概念

廣義:

數據結構指的是一組數據的存儲結構,算法指的是操作數據的一組方法.

例如:圖書館裏一般會將書分類放,然後有相應的編號規則.這就是數據結構,我們具體來查找這本書的方法就是算法,比如我們可以從頭到尾一本一本的找也可以按照分類,編號去找.

狹義:

數據結構就是指的一些著名的數據結構,比如,數組,鏈表,棧,堆,針對每種數據結構服務的方法比如二分查找這些就是算法.

兩者是相輔相成的.脫離各自都會沒有意義.

2.怎麼學習

(1).複雜度分析這個必須非常熟練

(2).20 個最常用的、最基礎數據結構與算法.

不管是應付面試還是工作需要,只要集中精力逐一攻克這 20 個知識點就足夠了。

這裏面有 10 個數據結構:數組、鏈表、棧、隊列、散列表、二叉樹、堆、跳錶、圖、Trie 樹;10 個算法:遞歸、排序、二分查找、搜索、哈希算法、貪心算法、分治算法、回溯算法、動態規劃、字符串匹配算法。

(3).不要只是死記硬背

要學習它的“來歷”“自身的特點”“適合解決的問題”以及“實際的應用場景”

(4).學習技巧

1. 邊學邊練,適度刷題,可以“適度”刷題,但一定不要浪費太多時間在刷題上。我們學習的目的還是掌握,然後應用

2. 多問、多思考、多互動

3. 打怪升級學習法,學習的過程中,我們碰到最大的問題就是,堅持不下來,我們在枯燥的學習過程中,也可以給自己設立一個切實可行的目標就像打怪升級一樣。

4.知識需要沉澱,不要想試圖一下子掌握所有,如果哪個知識點沒有怎麼學懂,不要着急,這是正常的。可以先沉澱一下,過幾天再重新學一遍。所謂,書讀百遍其義自見,我覺得是很有道理的!


 

第三章.複雜度分析(上):如何分析、統計算法的執行效率和資源消耗

1.大 O 時間複雜度表示法

所有代碼的執行時間 T(n) 與每行代碼的執行次數 n 成正比

T(n) 表示代碼執行的時間;n 表示數據規模的大小;f(n) 表示每行代碼執行的次數總和。因爲這是一個公式,所以用 f(n) 來表示。公式中的 O,表示代碼的執行時間 T(n) 與 f(n) 表達式成正比。

公式中的低階、常量、係數三部分並不左右增長趨勢,所以都可以忽略。我們只需要記錄一個最大量級就可以了

例如:T(n) = O(2n^2+n+100)  用大O表達法表示T(n) = O(n);

我們直接可以忽略低階的n 常數的100 和2n^2中的係數2

大 O 時間複雜度實際上並不具體表示代碼真正的執行時間,而是表示代碼執行時間隨數據規模增長的變化趨勢,所以,也叫作漸進時間複雜度(asymptotic time complexity),簡稱時間複雜度

2.時間複雜度分析

1. 只關注循環執行次數最多的一段代碼

2. 加法法則:總複雜度等於量級最大的那段代碼的複雜度

如果 T1(n)=O(f(n)),T2(n)=O(g(n));那麼 T(n)=T1(n)+T2(n)=max(O(f(n)), O(g(n))) =O(max(f(n), g(n))).

3. 乘法法則:嵌套代碼的複雜度等於嵌套內外代碼複雜度的乘積

假設 T1(n) = O(n),T2(n) = O(n2),則 T1(n) * T2(n) = O(n3)。我們可以把乘法法則看成是嵌套循環

3.常見時間複雜度

多項式量級非多項式量級

其中,非多項式量級只有兩個:O(2^n) 和 O(n!)。

數據規模 n 越來越大時,非多項式量級算法的執行時間會急劇增加,求解問題的執行時間會無限增長。所以,非多項式時間複雜度的算法其實是非常低效的算法

1. O(1)

一般情況下,只要算法中不存在循環語句、遞歸語句,即使有成千上萬行的代碼,其時間複雜度也是Ο(1)

2. O(logn)、O(nlogn)

i=1;
 while (i <= n)  {
   i = i * 2;
 }

變量 i 的值從 1 開始取,每循環一次就乘以 2 。當大於 n 時,循環結束。

2^x=n    所以執行次數x=log2n  比如n=4的時候 x=2  執行了2次

所以T(n) = O(log2n)

 i=1;
 while (i <= n)  {
   i = i * 3;
 }

上面代碼換成3  得出結果就是log(3)n=log(3)2*log(2)n 

計算過程(好久沒用都忘記了....)

忽略掉常數,結果同樣爲log2n

至於O(nlog2n) 就是外層多了一個嵌套循環,執行n次 這裏套用乘法規則嵌套代碼的複雜度等於嵌套內外代碼複雜度的乘積


 

第四章.複雜度分析(下):淺析最好、最壞、平均、均攤時間複雜度

// n 表示數組 array 的長度
int find(int[] array, int n, int x) {
  int i = 0;
  int pos = -1;
  for (; i < n; ++i) {
    if (array[i] == x) {
       pos = i;
       break;
    }
  }
  return pos;
}

最好情況時間複雜度就是,在最理想的情況下,執行這段代碼的時間複雜度

在最理想的情況下,要查找的變量 x 正好是數組的第一個元素,這時候最好情況就是O(1)

最壞情況時間複雜度就是,在最糟糕的情況下,執行這段代碼的時間複雜度

如果數組中沒有要查找的變量 x,我們需要把整個數組都遍歷一遍纔行這時候最壞情況就是O(n)

平均情況時間複雜度

有 n+1 種情況:在數組的 0~n-1 位置中不在數組中。我們把每種情況下,查找需要遍歷的元素個數累加起來,然後再除以 n+1,就可以得到需要遍歷的元素個數的平均值,即:

我們知道,時間複雜度的大 O 標記法中,可以省略掉係數、低階、常量,所以,咱們把剛剛這個公式簡化之後,得到的平均時間複雜度就是 O(n)。

但是計算過程稍微有點兒問題。究竟是什麼問題呢?我們剛講的這 n+1 種情況,出現的概率並不是一樣的。

我們知道,要查找的變量 x,要麼在數組裏,要麼就不在數組裏。這兩種情況對應的概率統計起來很麻煩,爲了方便你理解,我們假設在數組中與不在數組中的概率都爲 1/2。另外,要查找的數據出現在 0~n-1 這 n 個位置的概率也是一樣的,爲 1/n。所以,根據概率乘法法則,要查找的數據出現在 0~n-1 中任意位置的概率就是 1/(2n)。

因此,前面的推導過程中存在的最大問題就是,沒有將各種情況發生的概率考慮進去。如果我們把每種情況發生的概率也考慮進去,那平均時間複雜度的計算過程就變成了這樣:

這個值就是概率論中的加權平均值,也叫作期望值,所以平均時間複雜度的全稱應該叫加權平均時間複雜度或者期望時間複雜度

引入概率之後,前面那段代碼的加權平均值爲 (3n+1)/4。用大 O 表示法來表示,去掉係數和常量,這段代碼的加權平均時間複雜度仍然是 O(n)。

實際上,在大多數情況下,我們並不需要區分最好、最壞、平均情況時間複雜度三種情況。很多時候,我們使用一個複雜度就可以滿足需求了。只有同一塊代碼在不同的情況下,時間複雜度有量級的差距,我們纔會使用這三種複雜度表示法來區分。

均攤時間複雜度(攤還分析法)

對一個數據結構進行一組連續操作中,大部分情況下時間複雜度都很低,只有個別情況下時間複雜度比較高,而且這些操作之間存在前後連貫的時序關係,這個時候,我們就可以將這一組操作放在一塊兒分析,看是否能將較高時間複雜度那次操作的耗時,平攤到其他那些時間複雜度比較低的操作上。而且,在能夠應用均攤時間複雜度分析的場合,一般均攤時間複雜度就等於最好情況時間複雜度。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章