數據結構與算法 #Day1~3 #緒論 #時間複雜度 #空間複雜度

Day1 緒論

1. 數據結構

1.1 數據結構是什麼

什麼是數據結構?

  • 程序設計 = 數據結構 + 算法
  • 數據結構: 數據元素相互之間存在的一種或多種特定關係的集合

傳統上:數據結構 = 邏輯結構 + 物理結構

  • 邏輯結構:數據對象中數據元素中的相互關係
  • 物理結構:數據的邏輯結構在計算機中的存儲形式。

1.2 邏輯結構

  1. 集合結構: 除了屬於一個集合外,沒有其它任何關係
    在這裏插入圖片描述

  2. 線性結構:
    在這裏插入圖片描述

  3. 樹型結構:
    在這裏插入圖片描述

  4. 圖形關係:
    在這裏插入圖片描述

1.3 物理結構

物理結構,研究的是,如何把數據元素存儲到計算機的存儲器中。

  • 存儲器主要是針對內存而言,外部存儲器常用文件結構來描述。
  • 數據元素存儲形式有兩種:順序存儲鏈式存儲

  • 順序存儲結構: 把元素存儲在地址連續的存儲單元裏,數據間的邏輯關係和物理單元是一致的。(下圖是物理結構,表現爲緊挨着
    在這裏插入圖片描述

  • 鏈式存儲結構: 數據元素存放在任意的存儲單元裏,然後在這一個數據元素上放一個指針,來記錄下一個數據元素的地址。(因爲存放了指針,所以更費空間)
    在這裏插入圖片描述

2. 算法

算法就是解決問題的技巧和方式。

  • 如何計算 1 + 2 + … + 99 + 100 的值?有的人可能會一個個加過去,有的人會一個個加過去,有的人會使用等差數列(這個算法是高斯在小學發明的)。
  • 一個問題可以由多個算法解決,一個算法不可能有通解所有問題的能力。

2.1 算法的五個基本特徵

  1. 輸入
  2. 輸出
  3. 有窮性
  4. 確定性
    算法的每一條步驟都有確定的含義,不會出現二義性,不會有歧義。
    算法在一定條件下,只有一條執行路徑。相同的輸入只能有一個輸出的結果。
  5. 可行性
    每一步都可以在當前環境下執行有限次數完成(算法可以註明自己需要的環境)

2.2 算法設計的要求

1. 正確性 (大致分爲四層次)

  1. 算法程序沒有語法錯誤
  2. 算法程序對於合法輸入能夠產生滿足要求的輸出
  3. 算法程序對於非法輸入能夠產生滿足要求的說明
  4. 算法程序對於故意刁難的測試輸入都有滿足要求的輸出結果

2. 可讀性

  1. 算法設計的另一目的是爲了便於閱讀理解和交流
  2. 寫代碼的目的,一方面是爲了讓自己理解執行。另一方面是爲了便於自己或他人閱讀修改。

3. 時間效率高 & 存儲量低

Day2~3 時間複雜度&空間複雜度

1. 學習前準備

如何計算算法效率呢?

1.1 兩種計算方法

1.1.1 事後統計方法

需要事先編制好測試程序。利用計算機計時器對不同算法編制的運行時間做比較,從而確定算法效率的高低。

  • 缺點:
  1. 編制測試程序需要花費時間和精力。
  2. 不同測試環境的差別還非常之大。

1.1.2 事前分析估算方法

在計算機重新編寫前,依據統計方法對算法進行估算。

  • 影響因素:
  1. 算法的策略,方案
  2. 編譯參數的代碼質量
  3. 問題的輸入規模
  4. 機器執行指令的速度

由此可見: 除了軟硬件之外,就是算法的好壞和問題的輸入規模。

1.2 爲什麼只用高階階數

1.2.1 示例

計算1~100相加

  • 算法1:
for(int i = 1, n = 100, sum = 0; i <= n; i++) {  // 執行 1+n+1 次
    sum = sum + i;  // 執行 n 次
}
  1. 初始化方法執行 1 次
  2. 條件判斷執行 n+1 次(1次是判斷不成功時跳出循環)
  3. 函數體執行n次

總共 2n + 2 次


  • 算法2:
int sum = 0, n = 100;  // 執行1次
sum = (1 + n) * n / 2;  // 執行1次

總共 2 次

1.2.2 說明

  1. 不關心編寫程序的語言是什麼,也不關心程序跑在什麼樣的計算機上,只關心實現的算法。
  2. 最重要的是,把一系列的步驟抽象出來。
  3. 把基本操作的數量和輸入模式關聯起來。

所以上面的算法1和算法2的關係,是n和1的關係。

在這裏插入圖片描述

1.2.3 函數的漸進增長

給定兩個函數f(n) 和 g(n),如果存在一個整數N,使得對於所有的n > N, f(n) 總是比g(n)大,那麼,我們說f(n)的漸進增長快於g(n)。
在這裏插入圖片描述

  • f(n) = 3n + 1
  • g(n) = 2n + 3
  • N = 2
  • 存在n > N時,f(n) > g(n),所以f(n)的漸進增長快於g(n)

可以使用相關工具(如Excel或matlab),進行繪圖。會發現,當n足夠大時,似乎只與最高階階數相關,其它因素可以忽略不計。
判斷一個算法效率時: 函數中的常數和次要項常常快於忽略,而更應該關注主項(最高項)的階數。

2. 時間複雜度

2.1 大O表示法

在進行算法分析時,語句總的執行次數 T(n)是關於問題規模 n的函數,進而分析T(n)隨n的變化情況並確定T(n)的數量級 。算法的時間複雜度,也就是時間量度,記做:T(n)=O(f(n))T(n) = O(f(n))它表示隨問題規模n的增大,算法執行時間的增長率和f(n)的增長率 相同,稱作算法的漸進時間複雜度,簡稱爲時間複雜度 。其中f(n)是問題規模n的某個函數。(意思是,看重潛力,而不是當前。)

  • O()O()來體現算法時間複雜度的記法,被稱爲大O表示法
  • 隨着輸入規模n的增大,T(n)增長最慢的算法,一般爲最優算法。
  • 1.2.2中,三條曲線算法的時間複雜度分別爲O(1)O(1)O(n)O(n)O(n2)O(n^2)

2.2 萬能公式

關鍵是輸入n與輸出T(n)的關係

  1. 聚焦最高次項,其它全扔
  2. 最高次項常數變爲1

  • 常數階:最高階爲常數,O(1)O(1)
  • 線性階:最高階爲一次,O(n)O(n)。一般涉及非嵌套循環。
  • 平方階:最高階爲兩次,O(n2)O(n^2)。一般涉及一個嵌套的循環。
  • 對數階:log2nlog_2n,這個寫法的時間複雜度爲O(logn)O(logn)

注: 推導大O並不難,關鍵是對數列的一些相關運算,更多地考慮數學知識與能力。

3. 函數調用的時間複雜度分析

3.1 Demo1

int i, j;  // 1
for(i = 0; i < n; i++) {  // n + 1
    fuction(i);  // 1
}
product static void function(int count) {
    System.out.println(count);  // 1
}

1 + n + 1 + n * 1 ⇒ 2n +2
所以,時間複雜度是:O(n)O(n)

3.2 Demo2

int i, j;  // 1
for(i = 0; i < n; i++) {  // n + 1
    fuction(i);  // n
}
product static void function(int count) {
    int j;  // 1
    for(j = count; j < n; j++) {  // O(n)
		System.out.println(j);
	}
}

裏面那個循環,一眼瞄上去,執行次數就是一個等差數列。不管它是從n還是n+1還是n-1開始,也不管它是1或2或0結束。總歸是等差數列,最高階是 n2\frac n 2,也就是O(n)O(n)。(作爲程序員,需要的是抽象的概括能力。比如這裏,一眼瞄過去知道是等差,立馬就是一個O(n)O(n)甩過去。)
1 + n + 1 + n * O(n) ⇒ O(n2)O(n^2)

3.3 Demo3

n++;  // 1
function(n);  // n * O(n)
for(i = 0; i < n; i++) {  // n+2
	function(i);  // O(n) * (n + 1)
}
for(i = 0; i < n; i++) {  // O(n^2)
	for(j = i; j < n; j++) {
		System.out.println(j)
	}
}
product static void function(int count) {
    int j;  // 1
    for(j = count; j < n; j++) {  // O(n)
		System.out.println(j);
	}
}

(1+O(n2)+O(n2)+O(n2))(1 + O(n^2) + O(n^2) + O(n^2))O(n2)O(n^2)

4. 常見時間複雜度

時間複雜度(升序) 術語 舉例
O(1)O(1) 常數階 1
O(logn)O(logn) 對數階 3log2n+13log_2n +1
O(n)O(n) 線性階 2n + 1
O(nlogn)O(nlogn) nlogn階 4n+3nlog2n+14n +3nlog_2n +1
O(n2)O(n^2) 平方階 3n2+2n+13n^2 + 2n + 1
O(n3)O(n^3) 立方階 4n3+3n2+2n+14n^3 + 3n^2 + 2n + 1
O(2n)O(2^n) 指數階 2n2^n
O(n!)O(n!) 階乘 n!
O(nn)O(n^n) - -

由與O(n3)O(n^3)太大了,我們沒有討論它們的意義。我們只探究前5個。
在這裏插入圖片描述
在這裏插入圖片描述

5. 最壞情況&平均情況

  • 最壞情況: 窮盡到最後一次纔得到自己想要的結果
  • 平均情況: 期望的結果

  • 最壞運行時間: 是一種保證,在應用中,這是一種最重要的需求,如果沒特別指定,我們提到的運行時間都是最壞情況的運行時間。
  • 平均運行時間: 期望的運行時間

6. 空間複雜度

6.1 什麼是空間複雜度

寫代碼時,可以用空間時間

  • 判斷一個年份是否是閏年時,可以採取兩種方式:
  1. 寫一個判斷的函數,經過計算,返回結果(省空間,耗時間)
  2. 寫一個大型的數組,輸入年份,直接返回結果(省時間,耗空間)

6.2

空間複雜度計算公式:S(n)=O(f(n))S(n) = O(f(n))

n爲問題規模
f(n)爲語句關於n所佔存儲空間的函數

  • 我們用時間複雜度 來指運行時間的需求,空間複雜度 來指空間需求。(一般指的複雜度是指時間複雜度
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章