1. 程式人生 > >資料結構與演算法——學習整理記錄

資料結構與演算法——學習整理記錄

===注:此文由本人結合網上資源整理總結而來,僅代表個人的學習與理解,如有錯漏,歡迎指正!===

# 1. 資料結構

## 1.1 資料結構是什麼?

資料結構,直白地理解,就是研究資料的邏輯關係與儲存方式的一門學科。

可以簡單的分為:資料的邏輯結構(邏輯關係)和資料的儲存結構(物理結構)。

它是以某種形式將資料組織在一起的集合,它不僅儲存資料,還支援訪問和處理資料的操作。

### 1.1.1 資料的邏輯結構

資料的邏輯結構,簡單地理解,就是指的資料之間的邏輯關係

資料之間的邏輯關係可簡單的分為三類:

  • 一對一
  • 一對多
  • 多對多

### 1.1.1 資料的儲存結構

資料的儲存結構,也就是物理結構,指的是資料在物理儲存空間上的存放方式,可以選擇集中存放,還是分散存放

假設要儲存大小為 10M 的資料,則集中存放就如圖1(a)所示,分散存放就如圖1(b)所示。

圖1(a)集中存放

圖1(a) 資料的集中存放
圖1(b) 資料的分散存放

圖1(b) 資料的分散存放

## 1.2 為什麼要用資料結構?

使用資料結構的目的,即方便對資料的利用和操作。

## 1.3 資料結構有哪些?

根據資料的邏輯結構和儲存結構,可以把資料結構分為三大類:

  • 第一類:線性表,用於儲存具有“一對一”邏輯關係的資料。包括順序表、連結串列、棧和佇列;
  • 第二類:樹結構,用於儲存具有“一對多”邏輯關係的資料。包括普通樹,二叉樹,線索二叉樹等;
  • 第三類:圖結構,用於儲存具有“多對多”邏輯關係的資料;

## 1.4 如何使用資料結構?

可以通過分析資料之間的邏輯關係來決定使用哪種儲存結構,但具體使用順序儲存還是鏈式儲存,還要通過資料的物理結構來決定。

如果選擇集中儲存,就使用順序儲存結構;反之,就使用鏈式儲存。至於如何選擇,主要取決於儲存裝置的狀態以及資料的用途。

資料的用途不同,選擇的儲存結構也不同。將資料進行集中儲存有利於後期對資料進行遍歷操作,而分散儲存更有利於後期增加或刪除資料。因此,如果後期需要對資料進行大量的檢索(遍歷),就選擇集中儲存;反之,若後期需要對資料做進一步更新(增加或刪除),則選擇分散儲存。

# 2. 演算法

## 2.1 演算法是什麼:

演算法,從表面意思來理解,即解決問題的方法。

演算法是為求解一個問題需要遵循的、被清楚指定的簡單指令的集合。

在計算機中,演算法是指解決方案的準確而完整的描述。

### 2.1.1 演算法與程式

演算法是解決某個問題的想法、思路;而程式是在心中有演算法的前提下編寫出來的可以執行的程式碼。

某種程度上,演算法相當於是程式的雛形。

當解決問題時,首先心中要有解決問題的演算法,圍繞演算法編寫出程式程式碼。

## 2.2 演算法設計的要求

準確性、健壯性、執行效率(複雜度)

對於一個問題,想出解決的演算法,不一定就能解決這個問題。為了避免這種情況的發生,要充分全面地思考問題,儘可能地考慮到所有地可能情況,慎重選擇演算法(需要在實踐中不斷地積累經驗)。

### 2.2.1 演算法的執行效率(複雜度)

演算法的執行效率(複雜度)體現在兩方面:

  • 演算法的執行時間。(稱為“時間複雜度”)
  • 執行演算法所需的記憶體空間大小。(稱為“空間複雜度”)

#### 拿時間換空間,用空間換時間

演算法的時間複雜度和空間複雜度是可以相互轉化的。

比如:谷歌瀏覽器相比於其他的瀏覽器,執行速度要快。是因為它佔用了更多的記憶體空間,以空間換取了時間。

## 2.3 一個好演算法的標準

在符合演算法本身的要求(準確性和健壯性)的基礎上,使用演算法編寫的程式執行的時間短,執行過程中佔用的記憶體空間少,就可以稱這個演算法是“好演算法”。

## 2.4 演算法時間複雜度計算

計算一個演算法的時間複雜度,不可能把所有的演算法都編寫出實際的程式出來讓計算機跑,這樣會做很多無用功,效率太低。實際採用的方法是估算演算法的時間複雜度。

程式(C語言)一般由三種結構構成:順序結構、分支結構和迴圈結構。順序結構和分支結構中的每段程式碼只執行一次;迴圈結構中的程式碼的執行時間要看迴圈的次數。

由於是估算演算法的時間複雜度,相比而言,迴圈結構對演算法的執行時間影響更大。所以,演算法的時間複雜度,主要看演算法中使用到的迴圈結構中程式碼迴圈的次數(稱為“頻度”)。次數越少,演算法的時間複雜度越低。

### 2.4.1 時間複雜度的表示

演算法的時間複雜度的表示方式為:

O(頻度)

這種表示方式稱為大“O”記法
——注意,是大寫的字母O,不是數字0。

例如:

a) ++x; s=0;

b) for (int i=1; i<=n; i++) { ++x; s+=x; }

c) for (int i=1; i<=n; i++) { for (int j=1; i<=n; j++) { ++x; s+=x; } }

上邊這個例子中,a 程式碼的運行了 1 次,b 程式碼的運行了 n 次,c 程式碼運行了 n*n 次。

所以使用演算法的時間複雜度表示為:

a 的時間複雜度為O(1),b 的時間複雜度為O(n),c 的時間複雜度為為O(n2)。

如果把a、b、c三個例子組成一段程式,那麼演算法的時間複雜度為O(n2+n+1)。但這麼表示是不對的,還需要對n2+n+1進行簡化。

簡化的過程總結為3步:

  • 去掉執行時間中的所有加法常數。(例如 n2+n+1,直接變為 n2+n)
  • 只保留最高項。(例如 n2+n 變成 n2
  • 如果最高項存在但是係數不是1,去掉係數。(例如 n2 係數為 1)

所以,最終a、b和c合併而成的程式碼的時間複雜度為O(n2)。

## 2.5 常用的時間複雜度的排序

列舉了幾種常見的演算法時間複雜度的比較(又小到大):

O(1)常數階 < O(logn)對數階 < O(n)線性階 < O(n2)平方階 < O(n3)(立方階) < O(2n) (指數階)

3. 資料結構與演算法關係

可以從分析問題的角度去理清資料結構和演算法之間的關係。

通常,每個問題的解決都經過以下兩個步驟:

  • 第一步:分析問題,從問題中提取出有價值的資料,將其儲存;
  • 第二步:對儲存的資料進行處理,最終得出問題的答案;

資料結構負責解決第一個問題,即資料的儲存問題。針對資料不同的邏輯結構和物理結構,可以選出最優的資料儲存結構來儲存資料。而演算法負責為剩下的第二個問題提供解決方案。

總而言之,資料結構用於解決資料儲存問題,而演算法用於處理和分析資料,它們是完全不同的兩類學科。

# 參考