第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列...

42
0-6 目錄 1 導論 1-1 認識資料結構 ................................................................................................................. 1-2 1-2 認識演算法 ..................................................................................................................... 1-4 1-2-1 構思演算法 ...................................................................................................... 1-5 1-2-2 演算法的結構 .................................................................................................. 1-9 1-2-3 演算法的表示方式 ........................................................................................ 1-10 1-2-4 反覆與遞迴 .................................................................................................... 1-13 1-3 抽象資料型別 (ADT) ................................................................................................... 1-18 1-4 程式的效能分析 ........................................................................................................... 1-20 1-4-1 空間複雜度 .................................................................................................... 1-20 1-4-2 時間複雜度 .................................................................................................... 1-22 1-4-3 漸進符號 (O、Ω、Θ) ................................................................................. 1-25 2 陣列 2-1 認識陣列 ......................................................................................................................... 2-2 2-1-1 一維陣列........................................................................................................... 2-2 2-1-2 二維陣列........................................................................................................... 2-4 2-1-3 三維陣列........................................................................................................... 2-5 2-2 陣列的運算 ..................................................................................................................... 2-6 2-3 陣列的定址方式 ........................................................................................................... 2-14 2-4 陣列的應用 ................................................................................................................... 2-18 2-4-1 多項式 ............................................................................................................. 2-18 2-4-2 稀疏矩陣......................................................................................................... 2-23 2-5 字串 ................................................................................................................................ 2-27 2-6 結構 ................................................................................................................................ 2-32

Upload: others

Post on 21-Oct-2019

8 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

0-6

目錄

第 1 章 導論

1-1 認識資料結構 .................................................................................................................1-2

1-2 認識演算法 .....................................................................................................................1-4

1-2-1 構思演算法 ......................................................................................................1-5

1-2-2 演算法的結構 ..................................................................................................1-9

1-2-3 演算法的表示方式 ........................................................................................ 1-10

1-2-4 反覆與遞迴 .................................................................................................... 1-13

1-3 抽象資料型別 (ADT)................................................................................................... 1-18

1-4 程式的效能分析 ........................................................................................................... 1-20

1-4-1 空間複雜度 .................................................................................................... 1-20

1-4-2 時間複雜度 .................................................................................................... 1-22

1-4-3 漸進符號 (O、Ω、Θ)................................................................................. 1-25

第 2 章 陣列

2-1 認識陣列 .........................................................................................................................2-2

2-1-1 一維陣列 ...........................................................................................................2-2

2-1-2 二維陣列 ...........................................................................................................2-4

2-1-3 三維陣列 ...........................................................................................................2-5

2-2 陣列的運算 .....................................................................................................................2-6

2-3 陣列的定址方式 ........................................................................................................... 2-14

2-4 陣列的應用 ................................................................................................................... 2-18

2-4-1 多項式 ............................................................................................................. 2-18

2-4-2 稀疏矩陣 ......................................................................................................... 2-23

2-5 字串 ................................................................................................................................ 2-27

2-6 結構 ................................................................................................................................ 2-32

Page 2: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

0-7

目錄

第 3 章 鏈結串列

3-1 單向鏈結串列 .................................................................................................................3-2

3-1-1 宣告節點的結構 ..............................................................................................3-4

3-1-2 插入節點 ...........................................................................................................3-6

3-1-3 建立串列 ...........................................................................................................3-8

3-1-4 刪除節點 ......................................................................................................... 3-10

3-1-5 串列長度 ......................................................................................................... 3-12

3-1-6 串列連接 ......................................................................................................... 3-13

3-1-7 串列反轉 ......................................................................................................... 3-14

3-1-8 環狀鏈結串列 ................................................................................................ 3-16

3-2 雙向鏈結串列 ............................................................................................................... 3-19

3-2-1 宣告節點的結構 ............................................................................................ 3-20

3-2-2 插入節點 ......................................................................................................... 3-22

3-2-3 刪除節點 ......................................................................................................... 3-24

3-3 鏈結串列的應用 ........................................................................................................... 3-26

第 4 章 堆疊

4-1 認識堆疊 .........................................................................................................................4-2

4-2 堆疊的實作 .....................................................................................................................4-3

4-2-1 使用陣列實作堆疊 ..........................................................................................4-3

4-2-2 使用鏈結串列實作堆疊 ..................................................................................4-6

4-3 堆疊的應用 ................................................................................................................... 4-10

4-3-1 轉換運算式表示法 ........................................................................................ 4-10

4-3-2 計算後序表示法 ............................................................................................ 4-16

4-3-3 系統堆疊 ......................................................................................................... 4-20

4-3-4 遞迴 ................................................................................................................. 4-21

Page 3: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

0-8

第 5 章 佇列

5-1 認識佇列 .........................................................................................................................5-2

5-2 佇列的實作 .....................................................................................................................5-3

5-2-1 使用陣列實作佇列 ..........................................................................................5-3

5-2-2 使用鏈結串列實作佇列 ................................................................................ 5-10

5-3 雙向佇列 ....................................................................................................................... 5-13

第 6 章 樹狀結構

6-1 認識樹 .............................................................................................................................. 6-2

6-1-1 樹的相關名詞 ..................................................................................................6-3

6-1-2 樹的表示方式 ..................................................................................................6-6

6-2 二元樹 .............................................................................................................................. 6-8

6-2-1 完滿二元樹 V.S. 完整二元樹 ...................................................................... 6-11

6-2-2 二元樹的表示方式 ........................................................................................ 6-13

6-2-3 將一般樹轉換為二元樹 ................................................................................ 6-16

6-3 二元樹的走訪 ............................................................................................................... 6-18

6-3-1 中序走訪 ......................................................................................................... 6-18

6-3-2 前序走訪 ......................................................................................................... 6-19

6-3-3 後序走訪 ......................................................................................................... 6-20

6-3-4 決定唯一的二元樹 ........................................................................................ 6-22

6-4 二元樹的其它運算 ....................................................................................................... 6-24

6-4-1 複製二元樹 .................................................................................................... 6-24

6-4-2 比較二元樹 .................................................................................................... 6-25

6-5 運算式樹 ....................................................................................................................... 6-26

6-6 引線二元樹 ................................................................................................................... 6-27

6-6-1 節點結構 ......................................................................................................... 6-28

6-6-2 中序走訪 ......................................................................................................... 6-30

Page 4: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

0-9

目錄

6-6-3 插入節點 ......................................................................................................... 6-31

6-7 二元搜尋樹 ................................................................................................................... 6-34

6-7-1 搜尋節點 ......................................................................................................... 6-35

6-7-2 插入節點 ......................................................................................................... 6-37

6-7-3 刪除節點 ......................................................................................................... 6-43

6-8 霍夫曼樹 ....................................................................................................................... 6-46

6-9 樹林 ................................................................................................................................ 6-48

6-10 集合 ................................................................................................................................ 6-50

第 7 章 圖形結構

7-1 認識圖形 .........................................................................................................................7-2

7-1-1 圖形的定義 ......................................................................................................7-3

7-1-2 圖形的相關名詞 ..............................................................................................7-4

7-2 圖形的表示方式 .............................................................................................................7-7

7-2-1 相鄰矩陣 ...........................................................................................................7-7

7-2-2 相鄰串列 ...........................................................................................................7-9

7-2-3 加權圖形的表示方式 .................................................................................... 7-11

7-3 圖形的基本運算 ........................................................................................................... 7-14

7-3-1 深度優先搜尋 (DFS)..................................................................................... 7-14

7-3-2 廣度優先搜尋 (BFS) ..................................................................................... 7-21

7-3-3 連通單元 ......................................................................................................... 7-26

7-3-4 擴張樹 ............................................................................................................. 7-27

7-4 最小擴張樹 ................................................................................................................... 7-30

7-4-1 Kruskal 演算法................................................................................................ 7-30

7-4-2 Prim 演算法 .................................................................................................... 7-37

7-4-3 Sollin 演算法 .................................................................................................. 7-38

7-5 最短路徑 ....................................................................................................................... 7-40

Page 5: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

0-10

7-5-1 某個頂點到其它頂點的最短路徑 ............................................................... 7-40

7-5-2 任意兩個頂點的最短距離 ............................................................................ 7-48

7-6 拓樸排序 ....................................................................................................................... 7-52

第 8 章 排序

8-1 認識排序 .........................................................................................................................8-2

8-2 選擇排序 .........................................................................................................................8-3

8-3 插入排序 .........................................................................................................................8-6

8-4 氣泡排序 .........................................................................................................................8-9

8-5 謝耳排序 ....................................................................................................................... 8-12

8-6 快速排序 ....................................................................................................................... 8-15

8-7 合併排序 ....................................................................................................................... 8-19

8-8 基數排序 ....................................................................................................................... 8-25

8-9 二元樹排序 ................................................................................................................... 8-30

8-10 堆積排序 ....................................................................................................................... 8-33

8-10-1 最大堆積與最小堆積 .................................................................................... 8-33

8-10-2 堆積排序 ......................................................................................................... 8-46

8-10-3 Min-Max Heap.................................................................................................. 8-51

8-10-4 Deap ................................................................................................................. 8-57

第 9 章 搜尋

9-1 循序搜尋 .........................................................................................................................9-2

9-2 二元搜尋 .........................................................................................................................9-4

9-3 內插搜尋 .........................................................................................................................9-9

9-4 雜湊法 ............................................................................................................................ 9-12

9-4-1 雜湊函數 ......................................................................................................... 9-15

9-4-2 處理碰撞 ......................................................................................................... 9-18

Page 6: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

0-11

目錄

第 10 章 樹狀搜尋結構

10-1 AVL 樹 ........................................................................................................................... 10-2

10-1-1 LL 型 ............................................................................................................... 10-4

10-1-2 RR 型 ............................................................................................................... 10-6

10-1-3 LR 型 ............................................................................................................... 10-8

10-1-4 RL 型 ............................................................................................................. 10-12

10-2 2-3 樹 ............................................................................................................................ 10-16

10-2-1 搜尋鍵值 ....................................................................................................... 10-18

10-2-2 插入鍵值 ....................................................................................................... 10-19

10-2-3 刪除鍵值 ....................................................................................................... 10-21

10-3 2-3-4 樹 ........................................................................................................................ 10-24

10-4 B 樹 .............................................................................................................................. 10-29

10-4-1 m 元搜尋樹的定義 ...................................................................................... 10-29

10-4-2 B 樹的定義 ................................................................................................... 10-32

10-4-3 在 B 樹搜尋鍵值 .......................................................................................... 10-33

10-4-4 在 B 樹插入鍵值 .......................................................................................... 10-34

10-4-5 在 B 樹刪除鍵值 .......................................................................................... 10-36

Page 7: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

222CHAPTER

000222 CCCHHHAAAPPPTTTEEERRR

陣陣陣列列列

Page 8: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-1 認識陣列 2-4 陣列的應用

2-2 陣列的運算 2-5 字串

2-3 陣列的定址方式 2-6 結構

Page 9: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-2

2-1 認識陣列

對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您

可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

然您可能不知道隱含在陣列背後的原理及其實作方式,而事實上,瞭解這一切

不僅能幫助您活用各種不同維度的陣列,還能幫助您將陣列推廣至更多的用

途,例如實作其它抽象資料型別 (ADT),包括多項式、矩陣、字串、串列、堆

疊、佇列、樹、圖形…。

2-1-1 一維陣列

在開始介紹陣列之前,我們先來說明何謂變數 (variable),這是我們在程式中所

使用的一個名稱 (name),電腦會根據它的資料型別預留記憶體空間給它,然後

我們可以使用它來存放數值、字元、字串、指標等資料,稱為變數的值 (value)。

每個變數都只能存放一個資料,就像一個空紙箱一樣,如下圖所示。

陣列和變數一樣是用來存放資料,不同的是陣列雖然只有一個名稱,卻能存放

多個資料,就像一排空紙箱一樣,如下圖所示。陣列所存放的資料叫做元素

(element),每個元素有各自的值 (value)。至於陣列是如何區分它所存放的元素

呢?答案是透過索引 (index),諸如 C、C++、Java、C# 等程式語言預設是以索

引 0 代表陣列的第 1 個元素,索引 1 代表陣列的第 2 個元素,…,索引 n - 1 代

表陣列的第 n 個元素。

Page 10: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-3

陣列 000222

技術部落

當陣列最多能夠存放 n 個元素時,表示它的長度 (length) 為 n,而且除了一維陣

列 (one-dimension array),多數程式語言亦支援多維陣列 (multi-dimension array) 並

規定合法的維度上限。

此外,多數程式語言會限制陣列的元素必須是相同的資料型別,也就是同質陣

列 (homogeneous array),至於異質陣列 (heterogeneous array) 指的則是陣列的元素

可以是不同的資料型別。

指標與陣列

在 int *pA[3], A[3]; 敘述中,我們是使用 C語言宣告兩個長度均為 3 的陣列,不同的是陣列 pA 的三個元素 pA[0]、pA[1]、pA[2] 是用來存放 3 個指向整數的指標,而陣列 A的三個元素 A[0]、A[1]、A[2] 是用來存放 3 個整數,同時 A是指向 A[0] 的指標,A + i是指向 A[i] 的指標,換言之,A + i等於 &A[i],*(A + i) 等於 A[i]。

int A[3] = {10, 20, 30};

元素 值

A[0] 10

A[1] 20

A[2] 30

Page 11: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-4

2-1-2 二維陣列

二維陣列 (two-dimension array) 是一維陣列的延伸,如果說一維陣列是呈線性的

一度空間,那麼二維陣列就是呈平面的二度空間,而且任何平面的二維表格,

都可以使用二維陣列來存放,例如下圖是一個 m 列、n 行的成績單,而下面的敘

述則是宣告一個 m×n 的二維陣列來存放該成績單。

int A[m][n];

第 0 行 第 1 行 第 2 行 ⋯⋯ 第 n - 1 行

第 0 列

第 1 列

第 2 列

⋯⋯

第 m - 1 列

m×n 的二維陣列有兩個索引,第一個索引是從 0 到 m - 1,第二個索引是從 0 到

n - 1,總共可以存放 m×n 個元素,當我們要存取二維陣列時,就必須使用這兩

個索引,以上圖的成績單為例,我們可以使用這兩個索引將它表示成如下圖。

第 0 行 第 1 行 第 2 行 ⋯⋯ 第 n - 1 行

第 0 列

第 1 列

第 2 列

⋯⋯

第 m - 1 列

由上圖可知,「王小美」是存放在二維陣列內索引為 [1][0] 的位置,而「王小

美」的「數學」分數是存放在二維陣列內索引為 [1][n-1] 的位置;「張婷婷」

是存放在二維陣列內索引為 [m-1][0] 的位置,而「張婷婷」的「數學」分數是

存放在二維陣列內索引為 [m-1][n-1] 的位置,…餘此類推。

國語 自然 ⋯⋯ 數學

王小美 85 88 ⋯⋯ 77

孫大偉 99 86 ⋯⋯ 89

⋯⋯ ⋯⋯ ⋯⋯ ⋯⋯ ⋯⋯

張婷婷 75 92 ⋯⋯ 86

[0][0] [0][1] [0][2] ⋯⋯ [0][n-1]

[1][0] [1][1] [1][2] ⋯⋯ [1][n-1]

[2][0] [2][1] [2][2] ⋯⋯ [2][n-1]

⋯⋯ ⋯⋯ ⋯⋯ ⋯⋯ ⋯⋯

[m-1][0] [m-1][1] [m-1][2] ⋯⋯ [m-1][n-1]

Page 12: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-5

陣列 000222

技術部落

同樣的,我們也可以承襲空紙箱的概念來想像二維陣列,例如下圖是一個 2×3

的二維陣列,它在 x 方向的元素個數為 3 (索引為 0 ~ 2),y 方向的元素個數為

2 (索引為 0 ~ 1),總共可以存放 2×3 = 6 個元素。

2-1-3 三維陣列

三維陣列 (two-dimension array) 是具有三種維度的陣列,呈立體的三度空間,例

如下圖是一個 2×2×3 的三維陣列,它在 x 方向的元素個數為 3 (索引為 0 ~ 2),

y 方向的元素個數為 2 (索引為 0 ~ 1),z 方向的元素個數為 2 (索引為 0 ~ 1),

總共可以存放 2×2×3 = 12 個元素。

陣列的元素個數

一維陣列 A[upper0] 的元素個數為 upper0。

二維陣列 A[upper0][upper1] 的元素個數為 upper0×upper1。

三維陣列 A[upper0][upper1][upper2] 的元素個數為 upper0×upper1×

upper2。

n 維陣列 A[upper0][upper1]…[uppern-1] 的元素個數為 upper0×upper1

×…×uppern-1,即 。

Page 13: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-6

2-2 陣列的運算

一維陣列常見的運算

一維陣列常見的運算有下列幾種:

建立 (create):宣告陣列的資料型別與大小,以配置記憶體空間給它,例如

下面的敘述是宣告一個名稱為 A、資料型別為 int、元素個數為 10 的陣列:

int A[10];

讀取 (retrieve):取得陣列內索引為 i 之元素的值,例如下面的敘述是將陣列

內第一個元素的值指派給變數 X:

int X = A[0];

寫入 (store):設定陣列內索引為 i 之元素的值,例如下面的敘述是將陣列內

第一個元素的值設定為 100:

A[0] = 100;

插入 (insert):在陣列內索引為 i 的位置插入一個元素,而原來索引為 i 及之

後的元素均往後挪一個位置,例如:

將 10插入 A[4]

A[0] A[1] A[2]

記憶體 25 22

A[3] A[4]

8 19

A[5] A[6] A[7]

40 6

A[8] A[9]

15 55 32

A[0] A[1] A[2]

25 22

A[3] A[4]

8 19

A[5] A[6] A[7]

40 6

A[8] A[9]

15 55 32記憶體 10

A[0] A[1] A[2]

記憶體

A[3] A[4] A[5] A[6] A[7] A[8] A[9]

Page 14: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-7

陣列 000222

刪除 (delete):刪除陣列內索引為 i 的元素,而之後的元素均往前挪一個位

置,例如:

複製 (copy):將來源陣列的所有元素依序複製到目的陣列,例如:

搜尋 (search):在陣列內搜尋指定的元素是否存在,例如:

走訪 (traverse):從頭開始依序拜訪陣列的所有元素。

刪除 A[2]

A[0] A[1] A[2]

記憶體 25 22

A[3] A[4]

8 19

A[5] A[6] A[7]

40 6

A[8] A[9]

15 55 32

A[0] A[1] A[2]

25 22

A[3] A[4]

19

A[5] A[6] A[7]

40 6

A[8] A[9]

15 5532 記憶體

將陣列 A複製到陣列 B

A[0] A[1] A[2]

記憶體 25 22

A[3] A[4]

8 19

A[5] A[6] A[7]

40 6

A[8] A[9]

15 55 32

B0] B[1] B[2] B[3] B[4] B[5] B[6] B[7] B[8] B[9]

記憶體 25 22 8 19 40 6 15 5532

搜尋至此便停止(假設要搜尋的元素為 32)

A[0] A[1] A[2]

記憶體 25 22

A[3] A[4]

8 19

A[5] A[6] A[7]

40 6

A[8] A[9]

15 55 32

Page 15: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-8

範例 2.1:撰寫一個函數實作一維陣列的 [走訪 ] 運算並分析其時間複雜度。

解答: 這個函數的時間複雜度為 O(n)。

\Ch02\ex2_1.c

/*假設陣列 A有 n個元素,這個函數會印出陣列的所有元素*/

array_traverse(int A[], int n)

{

int i;

for(i = 0; i < n; i++)

printf("%d\n", A[i]);

}

main()

{

int A[5] = {10, 20, 30, 40, 50};

array_traverse(A, 5);

}

範例 2.2:撰寫一個函數實作一維陣列的 [插入 ] 運算並分析其時間複雜度。

解答: 假設索引 i 落在 0 ~ n - 1 的機率均相同,則元素移動次數的期望值為

((n - 1) + (n - 2) + … + 1 + 0) / n = (n - 1) / 2 = O(n)。<\Ch02\ex2_2.c>

/*假設陣列 A有 n個元素,這個函數會在陣列內索引為 i的位置插入一個元素 value*/

array_insert(int A[], int n, int i, int value)

{

int j;

if (i < 0 || i >= n) return; /*若索引 i超過陣列的合法範圍,則返回*/

for(j = n - 1; j > i; j--) /*將原來索引為 i及之後的元素均往後挪一個位置*/

A[j] = A[j - 1];

A[i] = value; /*在索引為 i的位置插入一個元素 value*/

}

Page 16: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-9

陣列 000222

範例 2.3:撰寫一個函數實作一維陣列的 [刪除 ] 運算並分析其時間複雜度。

解答: 假設索引 i 落在 0 ~ n - 1 的機率均相同,則元素移動次數的期望值為

((n - 1) + (n - 2) + … + 1 + 0) / n = (n - 1) / 2 = O(n)。<\Ch02\ex2_3.c>

/*假設陣列 A有 n個元素,這個函數會刪除陣列內索引為 i的元素*/

array_delete(int A[], int n, int i)

{

int j;

if (i < 0 || i >= n) return; /*若索引 i超過陣列的合法範圍,則返回*/

for(j = i; j < n - 1; j++) /*將之後的元素均往前挪一個位置*/

A[j] = A[j + 1];

A[n - 1] = 0; /*將最後一個元素設定為 0*/

}

二維陣列常見的運算

二維陣列常見的運算有下列幾種:

矩陣轉置 (matrix transposition):假設 A 為 m×n 矩陣,則 A 的轉置矩陣 B 為

n×m 矩陣,且 B 的第 i 列第 j 行元素等於 A 的第 j 列第 i 行元素,即 bij = aji。

例如:

A =

B = At =

Page 17: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-10

矩陣相加 (matrix addition):假設 A、B 均為 m×n 矩陣,則 A 與 B 相加得出

的 C 亦為 m×n 矩陣,且 C 的第 i 列第 j 行元素等於 A 的第 i 列第 j 行元素

加上 B 的第 i 列第 j 行元素,即 cij = aij + bij。

例如:

Page 18: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-11

陣列 000222

矩陣相乘 (matrix multiplication):假設 A 為 m×n 矩陣、B 為 n×p 矩陣,則 A

與 B 相乘得出的 C 為 m×p 矩陣,且 C 的第 i 列第 j 行元素等於 A 的第 i 列

乘上 B 的第 j 行 (兩個向量的內積),即 。

cij 等於 A 的第 i 列乘上 B 的第 j 行 (兩個向量的內積):

例如:

Page 19: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-12

範例 2.4:[矩陣走訪 ] 撰寫一個函數印出矩陣的所有元素並分析其時間複雜度。

解答: 這個函數的時間複雜度為 O(m x n)。

\Ch02\ex2_4.c

/*假設 A為 m×n 陣列,這個函數會印出二維陣列的所有元素*/

matrix_traverse(int m, int n, int A[m][n])

{

int i, j;

for(i = 0; i < m; i++){

for(j = 0; j < n; j++)

printf("%d ", A[i][j]);

printf("\n");

}

}

main()

{

int A[2][3] = {{11, 12, 13}, {21, 22, 23}};

matrix_traverse(2, 3, A);

}

範例 2.5: [矩陣轉置 ] 撰寫一個函數實作矩陣轉置運算並分析其時間複雜度。

解答: 這個函數的時間複雜度為 O(m x n)。<\Ch02\ex2_5.c>

/*假設 A為 m×n陣列,這個函數會計算 A的轉置矩陣 B,則 B為 n×m矩陣*/

matrix_transpose(int m, int n, int A[m][n], int B[n][m])

{

int i, j;

for(i = 0; i < m; i++)

for(j = 0; j < n; j++)

B[j][i] = A[i][j];

}

Page 20: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-13

陣列 000222

範例 2.6: [矩陣相加 ] 撰寫一個函數實作矩陣相加運算並分析其時間複雜度。

解答: 這個函數的時間複雜度為 O(m x n)。<\Ch02\ex2_6.c>

/*假設 A、B、C均為 m×n陣列,這個函數會計算 C=A+B*/

matrix_add(int m, int n, int A[m][n], int B[m][n], int C[m][n])

{

int i, j;

for(i = 0; i < m; i++)

for(j = 0; j < n; j++)

C[i][j] = A[i][j] + B[i][j];

}

1. 撰寫一個函數實作一維陣列的 [複製 ] 運算並分析其時間複雜度。

2. 撰寫一個函數實作一維陣列的 [搜尋 ] 運算並分析其時間複雜度,若搜尋到指定的元素,就傳回其索引,否則傳回 -1。

3. [矩陣相乘 ] 撰寫一個函數實作矩陣相乘運算並分析其時間複雜度。

提示:

for(i = 0; i < m; i++)

for(j = 0; j < p; j++){

C[i][j] = 0;

for(k = 0; k < n; k++)

C[i][j] += A[i][k] * B[k][j];

隨隨隨堂堂堂練練練習習習

Page 21: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-14

2-3 陣列的定址方式

一維陣列的定址方式

無論陣列的維度為何,在記憶體空間都是以連續位址的區塊來存放,也就是一

維的形式,而且存放順序分成以列為主 (row major order) 和以行為主 (column

major order) 兩種,前者是以列為基礎,一列一列依序配置記憶體位址,後者是

以行為基礎,一行一行依序配置記憶體位址。

首先,我們根據以列為主來討論一維陣列 A[upper0] 的定址方式,假設第一個元

素 A[0] 的位址為 α,則元素 A[i] 的位址為 α + i x length (元素 A[i] 的前面有 i

個元素,每個元素的大小為 length)。

例如下面的敘述是宣告一個能夠存放五個整數的一維陣列,其索引為 0 ~ 4,編

譯器一看到這個敘述,就會在記憶體空間配置五個連續位址的區塊給陣列 A,而

且每個區塊的大小剛好可以容納一個整數,如下圖所示。

int A[5];

二維陣列的定址方式

接著,我們根據以列為主來討論二維陣列 A[upper0][upper1] 的定址方式,假設第

一個元素 A[0][0] 的位址為 α,則元素 A[i][0] 的位址為 α + i x upper1 (元素 A[i][0]

的前面有 i 列,而每列各有 upper1個元素),元素 A[i][j] 的位址為 α + i x upper1 + j

(此處遵循 C 語言會自動計算元素大小的慣例,所以沒有乘上元素大小)。

元素 位址

A[0] α

A[1] α + 1 x sizeof(int)

A[2] α + 2 x sizeof(int)

A[3] α + 3 x sizeof(int)

A[4] α + 4 x sizeof(int)

A[0] A[1] A[2] A[3] A[4]

記憶體

起始位址為α

Page 22: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-15

陣列 000222

範例 2.7:假設以 C 語言宣告一個浮點數陣列 float A[7][8]; (已知 sizeof(float) 等

於 4),若元素 A[3][4] 在記憶體空間的位址為 100,則元素 A[5][6] 的

位址為何?而元素 A[2][3] 的位址又為何?

解答: A[5][6] 的位址 = A[3][4] 的位址 + 位移量

= 100 + ((5 x 8 + 6) - (3 x 8 + 4)) x 4

= 172

A[2][3] 的位址 = A[3][4] 的位址 + 位移量

= 100 + ((2 x 8 + 3) - (3 x 8 + 4)) x 4

= 64

範例 2.8:在以列為主的定址方式中,陣列 A[3][2] 在記憶體空間的存放順序為

A[0][0]、A[0][1]、A[1][0]、A[1][1]、A[2][0]、A[2][1],而在以行為主的

定址方式中,陣列 A[3][2] 在記憶體空間的存放順序將改為 A[0][0]、

A[1][0]、A[2][0]、A[0][1]、A[1][1]、A[2][1],試改用以行為主來討論二

維陣列 A[upper0][upper1] 的定址方式,假設第一個元素 A[0][0] 的位址

為 α,則元素 A[i][j] 的位址為何?

解答: 由於第一個元素 A[0][0] 的位址為 α,因此,元素 A[i][0] 的位址為 α + i,

而元素 A[i][j] 的位址為 α + j x upper0 + i (元素 A[0][j] 的前面有 j 行,而

每行各有 upper0個元素)(此處遵循 C 語言會自動計算元素大小的慣例,所

以沒有乘上元素大小)。

範例 2.9:假設以 C 語言宣告一個整數陣列 int A[8][5]; (已知 sizeof(int) 等於 2),

若元素 A[0][0] 在記憶體空間的位址為 100,且改用以行為主的定址方

式,則元素 A[6][4] 的位址為何?

解答: A[6][4] 的位址 = A[0][0] 的位址 + 位移量

= 100 + (4 x 8 + 6) x 2

= 176

Page 23: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-16

三維陣列的定址方式

繼續,我們根據以列為主來討論三維陣列 A[upper0][upper1][upper2] 的定址方式,

假設第一個元素 A[0][0][0] 的位址為 α,則元素 A[i][0][0] 的位址為 α + i x upper1 x

upper2,元素 A[i][j][0] 的位址為 α + i x upper1 x upper2 + j x upper2,元素 A[i][j][k] 的

位址為 α + i x upper1 x upper2 + j x upper2 + k (此處遵循 C語言會自動計算元素大小

的慣例,所以沒有乘上元素大小)。

範例 2.10:假設以 C語言宣告一個浮點數陣列 float A[6][7][8]; (已知 sizeof(float) 等

於 4),若元素 A[3][4][5] 在記憶體空間的位址為 1000,則元素 A[1][2][3]

的位址為何?

解答: 1000 + ((1 x 7 x 8 + 2 x 8 + 3) - (3 x 7 x 8 + 4 x 8 + 5)) x 4 = 480

n 維陣列的定址方式

最後,我們根據以列為主來討論 n 維陣列 A[upper0][upper1]…[uppern-1] 的定址方

式,假設第一個元素 A[0][0]…[0] 的位址為 α,則元素 A[i0][0]…[0] 的位址為:

α + i0 x upper1 x upper2 x … x uppern-1

元素 A[i0][i1][0]…[0] 的位址為: α + i0 x upper1 x upper2 x … x uppern-1

+ i1 x upper2 x upper3 x … x uppern-1

元素 A[i0][i1][i2]…[in-1] 的位址為:

Page 24: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-17

陣列 000222

範例 2.11:試以行為主來討論 n 維陣列 A[upper0][upper1]⋯[uppern-1] 的定址方式,

假設第一個元素 A[0][0]⋯[0] 的位址為α,則元素 A[i0][i1]⋯[in-1] 的位

址為何?

解答: 首先來討論二維陣列 A[upper0][upper1],假設第一個元素 A[0][0] 的位址為

α,則元素 A[i][0] 的位址為 α + i,元素 A[i][j] 的位址為 α + j x upper0 + i。

接下來推廣至三維陣列 A[upper0][upper1][upper2],假設第一個元素 A[0][0][0]

的位址為 α,則元素 A[i][0][0] 的位址為 α + i,元素 A[i][j][0] 的位址為 α

+ j x upper0 + i,元素 A[i][j][k] 的位址為 α + k x upper1 x upper0 + j x upper0 +

i。

最後推廣至 n 維陣列 A[upper0][upper1]…[uppern-1],假設第一個元素

A[0][0]…[0] 的位址為 α,則元素 A[i0][i1][i2]…[in-1] 的位址為:

1. 試寫出在以列為主的定址方式中,A[2][3][3] 在記憶體空間的存放順序,然後寫出在以行為主的定址方式中,陣列 A[2][3][3] 在記憶體空間的存放

順序。

2. 假設以 C 語言宣告一個整數陣列 int A[6][8][5]; (已知 sizeof(int) 等於 2),若元素 A[0][0][0] 在記憶體空間的位址為 100,且改用以行為主的定址方式,

則元素 A[3][6][4] 的位址為何?

3. 無論是採用以列為主或以行為主的定址方式,一個 4×4×4 的三維陣列中有幾個元素會被放在相同的位址?

隨隨隨堂堂堂練練練習習習

Page 25: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-18

2-4 陣列的應用

陣列本身不僅是資料結構,更可以用來實作其它抽象資料型別 (ADT),包括多

項式、矩陣、字串、串列、堆疊、佇列、樹、圖形…。

2-4-1 多項式

陣列可以用來存放多項式 (polynomial),常見的方式有下列幾種:

一、 使用陣列 Polynomial 存放多項式 cnXn + cn-1Xn-1 +… + c1X1 + c0X0,Polynomial[] =

{n, cn, cn-1, …, c1, c0},其中 n 為最高羃次,cn、cn-1、…、c1、c0為係數,如下

圖所示。

以 8X4 - 6X2 + 3X5 + 5 為例,我們先依照羃次由高至低排列寫出 3X5 + 8X4 +

0X3 - 6X2 + 0X1 + 5X0,於是得到 Polynomial[] = {5, 3, 8, 0, -6, 0, 5},這種方式

雖然簡單,但要是碰到類似 5X100 - 1 的多項式,將會浪費很多記憶體。

二、 使用陣列 Polynomial 存放多項式 cm-1Xem-1 + … + c1Xe1 + c0Xe0,Polynomial[] = {m,

cm-1, em-1, …, c1, e1, c0, e0},其中 m 為非零項的個數,cm-1、…、c1、c0為非零項

的係數,em-1、…、e1、e0為非零項的羃次且 em-1 >…> e1 > e0 ≥ 0,如下圖所示。

以 8X4 - 6X2 + 3X5 + 5 為例,我們先依照羃次由高至低排列寫出 3X5 + 8X4 -

6X2 + 5X0,於是得到 Polynomial[] = {4, 3, 5, 8, 4, -6, 2, 5, 0},這種方式尤其適

合存放類似 5X100 - 1 的多項式。

[0] [1] … [n]

n cn … c1

Polynomial

總共 n+2個元素

[2]

cn-1

[n+1]

c0

[0] [1] [2] …

m cm-1 em-1 …

Polynomial

總共 2m+1個元素

[2m]

e0

[2m-1]

c0

Page 26: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-19

陣列 000222

三、 定義如下的 NonZeroTerm 結構表示非零項,然後定義如下的 Polynomial 結構

表示多項式:

typedef struct{ /*定義表示非零項的結構*/

int coef; /*非零項的係數*/

int exp; /*非零項的羃次*/

}NonZeroTerm; #define MAX_SIZE 100 /*定義多項式最多包含 MAX_SIZE個非零項*/

typedef struct{ /*定義表示多項式的結構*/

int count; /*非零項的個數*/

NonZeroTerm terms[MAX_SIZE]; /*非零項*/

}Polynomial;

以 A(X) = 8X4 - 6X2 + 3X5 + 5 和 B(X) = 2X6 + 4X2 + 1 為例,我們可以先宣告

兩個型別為 Polynomial 結構的變數 A、B,代表這兩個多項式:

Polynomial A, B;

接下來依照羃次由高至低排列寫出 A(X) = 3X5 + 8X4 - 6X2 + 5X0和 B(X) = 2X6

+ 4X2 + 1X0,然後依照下表設定變數 A、B 的值:

count 4

[0] [1] [2] [3]

coef exp coef exp coef exp coef exp

terms

3 5 8 4 -6 2 5 0

count 3

[0] [1] [2]

coef exp coef exp coef exp

terms

2 6 4 2 1 0

Page 27: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-20

範例 2.12:[多項式相加 ] 使用第 2-4-1 節定義的 NonZeroTerm 結構 (非零項) 和

Polynomial 結構 (多項式),撰寫一個將兩個多項式相加的函數,然後

令它將 A(X) = 3X5 + 8X4 - 6X2 + 5 和 B(X) = 2X6 + 4X2 + 1 兩個多項式相

加,得到 C(X) = 2X6 + 3X5 + 8X4 - 2X2 + 6,再印出類似如下的結果。

解答: 假設 C(X) = A(X) + B(X),其運算法則如下:

1. 將 A(X)、B(X) 依照羃次由高至低進行掃瞄。

2. 比較 A(X)、B(X) 目前非零項的羃次,將羃次較大的非零項複製到

C(X),若羃次相等且相加後的係數和不等於零,則將相加後的非零

項複製到 C(X)。

3. 凡已經被複製到 C(X) 的非零項,其多項式就往前移動一項。

4. 重複 1. ~ 3.,直到兩個多項式的非零項都掃瞄完畢為止。

除了多項式相加函數之外,我們還撰寫了一個 attach() 函數,這個函數會

在多項式加入一個非零項,屆時不僅可以呼叫 attach() 函數進行 A(X) 和

B(X) 的初始化,同時可以將相加後的非零項加入 C(X)。

\Ch02\ex2_12.c (下頁續 1/3)

/*這個巨集用來比較 x、y,若 x < y,傳回 -1;若 x == y,傳回 0;若 x > y,傳回 1*/

#define COMPARE(x, y) ((x < y) ? -1 : (x == y) ? 0 : 1)

#define MAX_SIZE 100 /*定義多項式最多包含 MAX_SIZE個非零項*/

typedef struct{ /*定義表示非零項的結構*/

int coef; /*非零項的係數*/

int exp; /*非零項的羃次*/

}NonZeroTerm;

Page 28: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-21

陣列 000222

\Ch02\ex2_12.c (下頁續 2/3)

typedef struct{ /*定義表示多項式的結構*/

int count; /*非零項的個數*/

NonZeroTerm terms[MAX_SIZE]; /*非零項*/

}Polynomial; /*主程式*/

main()

{

Polynomial A, B, C; /*宣告三個型別為 Polynomial結構的變數 A、B、C*/

A.count = 0; /*將 A(X) = 3X5 + 8X4 - 6X2 + 5加以初始化*/

attach(&A, 3, 5);

attach(&A, 8, 4);

attach(&A, -6, 2);

attach(&A, 5, 0); B.count = 0; /*將 B(X) = 2X6 + 4X2 + 1加以初始化*/

attach(&B, 2, 6);

attach(&B, 4, 2);

attach(&B, 1, 0); PolyAdd(&A, &B, &C); /*呼叫函數計算 C(X) = A(X) + B(X)*/

int i; /*印出多項式 C(X) 的結果*/

printf("多項式 C(X)的非零項有%d個\n", C.count);

for(i = 0; i < C.count; i++)

printf("第%d個非零項的係數:%d\t羃次:%d\n", i+1, C.terms[i].coef, C.terms[i].exp);

} /*這個函數會在多項式加入一個非零項*/

attach(Polynomial *ptr, int coef, int exp)

{

if (ptr->count >= MAX_SIZE) return;

ptr->terms[ptr->count].coef = coef;

ptr->terms[ptr->count].exp = exp;

ptr->count++;

}

Page 29: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-22

\Ch02\ex2_12.c (接上頁 3/3)

/*這個函數會將兩個多項式相加,即 C(X) = A(X) + B(X)*/

PolyAdd(Polynomial *pA, Polynomial *pB, Polynomial *pC)

{

int currentA = 0, currentB = 0;

pC->count = 0;

while(currentA < pA->count && currentB < pB->count){

switch(COMPARE(pA->terms[currentA].exp, pB->terms[currentB].exp)){

/*當 A的羃次小於 B的羃次時,將 B的非零項加入多項式*/

case -1:

attach(pC, pB->terms[currentB].coef, pB->terms[currentB].exp);

currentB++;

break;

/*當 A的羃次等於 B的羃次時,將兩者相加後的非零項加入多項式*/

case 0:

if((pA->terms[currentA].coef + pB->terms[currentB].coef) != 0)

attach(pC, pA->terms[currentA].coef + pB->terms[currentB].coef, pA->terms[currentA].exp);

currentA++;

currentB++;

break;

/*當 A的羃次大於 B的羃次時,將 A的非零項加入多項式*/

case 1:

attach(pC, pA->terms[currentA].coef, pA->terms[currentA].exp);

currentA++; } }

while(currentA < pA->count){ /*將 A剩下的非零項加入多項式*/

attach(pC, pA->terms[currentA].coef, pA->terms[currentA].exp);

currentA++;

}

while(currentB < pB->count){ /*將 B剩下的非零項加入多項式*/

attach(pC, pB->terms[currentB].coef, pB->terms[currentB].exp);

currentB++;

}

}

Page 30: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-23

陣列 000222

2-4-2 稀疏矩陣

除了多項式之外,陣列也經常用來存放稀疏矩陣 (sparse matrix),也就是非零元

素相對較少的矩陣,例如:

當我們以二維陣列存放稀疏矩陣時,往往會浪費許多空間,因為稀疏矩陣大部

分的元素均為零,此時,我們可以定義如下結構存放稀疏矩陣的非零項:

#define MAX_SIZE 100 /*定義稀疏矩陣最多包含 MAX_SIZE個非零項*/

typedef struct{ /*定義表示非零項的結構*/

int row; /*非零項位於第幾列*/

int col; /*非零項位於第幾行*/

int value; /*非零項的值*/

}SparseTerm;

有了這個結構,我們就可以宣告一個陣列變數 A 代表前面的稀疏矩陣:

SparseTerm A[MAX_SIZE];

這個 4×5 稀疏矩陣有 6 個非零項,分別存放在 A[1] ~ A[6],而 A[0].row、A[0].col、

A[0].value 則是稀疏陣列的列數、行數及非零項的個數:

A [0] [1] [2] [3] [4] [5] [6]

row 4 0 0 1 2 2 3

col 5 1 4 3 0 2 4

value 6 1 2 3 4 5 6

A =

4×5

Page 31: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-24

範例 2.13:[稀疏矩陣轉置 ] 使用第 2-4-2 節定義的 SparseTerm 結構存放稀疏矩陣

的非零項,然後撰寫一個函數實作稀疏矩陣轉置運算並分析其時間複

雜度,下面是一個例子。

B [0] [1] [2] [3] [4] [5] [6]

row 5 0 1 2 3 4 4

col 4 2 0 2 1 0 3

value 6 4 1 5 3 2 6

解答: 由於稀疏矩陣 A 的行將轉置成為稀疏矩陣 B 的列,因此,我們是針對稀

疏矩陣 A 的每一行做轉置,一旦遇到非零項,就將它加入稀疏矩陣 B。

由於 SparseTranspose() 函數包含雙層迴圈,故其時間複雜度為 A[0].col x

A[0].value,即稀疏矩陣 A 的行數乘以非零項的個數,而在最差情況下,

非零項的個數為 A[0].row x A[0].col,於是得到時間複雜度為 O(row x

col2)。<\Ch02\ex2_13.c>

SparseTranspose(SparseTerm A[], SparseTerm B[]) /* B = At */

{

int currentB;

int i, j;

B[0].row = A[0].col;

Page 32: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-25

陣列 000222

B[0].col = A[0].row;

B[0].value = A[0].value;

if (B[0].value == 0) return; /*若稀疏矩陣沒有非零項,則返回*/

currentB = 1;

for(i = 0; i < A[0].col; i++) /*針對稀疏矩陣 A的每一行做轉置*/

for(j = 0; j <= B[0].value; j++) /*找出目前行的非零項*/

if (A[j].col == i){ /*將目前行的非零項加入稀疏矩陣 B*/

B[currentB].row = A[j].col;

B[currentB].col = A[j].row;

B[currentB].value = A[j].value;

currentB++;

}

}

範例 2.14:[下三角矩陣 ] 當我們使用二維陣列來存放如下圖的下三角矩陣 (lower

triangular matrix) 時,往往會浪費許多記憶體,因為它的對角線上方均

為 0,此時,可以改用一維陣列來存放非零項,假設以一維陣列 B 來

存放 n×n 的下三角矩陣 A,即 A[0][0] 存放在 B[0]、A[1][0] 存放在

B[1]、A[1][1] 存放在 B[2]…餘此類推,試問,陣列 B 的長度為何?又

A[i][j] 是存放在陣列 B 的哪個位置?

解答: 陣列B的長度為 1 + 2 + …+ n = (1 + n)n/2,而 A[i][j] 存放在 B[(1 + i)i/2 + j]。

Page 33: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-26

1. [稀疏矩陣相加 ] 使用第 2-4-2 節定義的 SparseTerm 結構存放稀疏矩陣的非零項,然後撰寫一個函數實作稀疏矩陣相加運算並分析其時間複雜度,

下面是一個例子。

2. [上三角矩陣 ] 當我們使用二維陣列來存放如下圖的上三角矩陣 (upper triangular matrix) 時,往往會浪費許多記憶體,因為它的對角線下方均為

0,此時,可以改用一維陣列來存放非零項,假設以一維陣列 D 來存放 n

×n 的上三角矩陣 C,即 C[0][0] 存放在 D[0]、C[0][1] 存放在 D[1]、…、C[0][n-1] 存放在 D [n-1]…餘此類推,試問,陣列 D 的長度為何?又 C[i][j] 是存放在陣列 D 的哪個位置?

隨隨隨堂堂堂練練練習習習

Page 34: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-27

陣列 000222

2-5 字串

對 C 語言來說,字串 (string) 是以空字元 '\0' 結尾的一串字元,因此是以字元

陣列的形式來表示字串。要注意的是當您指定字元的值時,必須在其前後加上

單引號,例如 'J',而當您指定字串的值時,必須在其前後加上雙引號 ",例如

"Happy"。

C 語言的每個字元佔用 1byte,至於字串的長度則取決於宣告時的字元陣列大小,

若宣告時沒有指定字元陣列大小,則由編譯器根據字串的值自動決定。下面的

敘述分別宣告了三個名稱為 name1、name2、name3,初始值為 "Jean Chen"、"Mary"、

"Joe" 的字元陣列 (即字串):

char name1[] = "Jean Chen";

char name2[10] = "Mary";

char name3[5] = {'J', 'o', 'e', '\0'};

這三個字元陣列的內容如下,由於在宣告 name1 時沒有指定字元陣列大小,故其

大小是由編譯器根據字串的值 "Jean Chen" 自動決定,即 9 (不包含空字元 '\0'):

正因為 C 語言的字串實際上是一個字元陣列,所以能夠透過陣列運算子 [] 存取

字串的某個字元,例如:

printf("%c", name1[3]); /*印出 name1字串的第四個字元,即 'n' */

name3[2] = 'y'; /*將 name3字串的第三個字元變更為 'y' */

[0] [1] [2]

name1 J e

[3] [4]

a n

[5] [6] [7]

C h

[8] [9]

e n \0

[0] [1] [2]

name2 M a

[3] [4]

r y

[5] [6] [7] [8] [9]

\0

[0] [1] [2]

name3 J o

[3] [4]

e \0

Page 35: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-28

字串常見的運算

字串常見的運算有下列幾種,事實上,這些運算幾乎都已經囊括在標準的 C 語

言函數庫,大部分是在 <string.h>,少數是在 <stdlib.h>:

字串長度:計算字串包含幾個字元,但不包含結尾的空字元 '\0',例如

strlen(char *str) 函數會傳回字串參數 str 包含幾個字元,其使用方式如下:

char str[] = "Jean Chen";

printf("%d", strlen(str)); /*印出 str字串的長度為 9,不包含結尾的空字元*/

字串複製:將一個字串複製給另一個字串,例如 strcpy(char *destination, char

*source) 函數會將字串參數 source 的內容複製到字串參數 destination 的陣

列,所以在複製之前務必確認 destination 的陣列大小足夠存放 source 的內

容,包括結尾的空字元在內,其使用方式如下:

char str[50];

strcpy(str, "Happy New Year"); /*將 "Happy New Year" 複製到 str字串*/

除了 strcpy() 函數之外,C 語言還提供了其它形式的字串複製函數,例如

strncpy()、strcat()、strncat()、strset()、strnset()…,有需要的讀者可以自行參考

C 語言專書。

字串比較:比較兩個字串,例如 strcmp(char *str1, char *str2) 函數會從頭開

始逐字比較 str1 和 str2 兩個字串,若碰上不同的字元,則比較其 ASCII 碼,

當 str1 大於 str2 時,將傳回正值,當 str1 等於 str2 時,將傳回 0,當 str1 小

於 str2 時,將傳回負值,其使用方式如下:

char str1[] = "Happy Birthday";

char str2[] = "Happy New Year";

printf("%d", strcmp(str1, str2)); /*印出 -1,因為 str1小於 str2*/

除了 strcmp() 函數之外,C 語言還提供了其它形式的字串比較函數,例如

strncmp()、strcmpi()、strncmpi()…。

Page 36: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-29

陣列 000222

字串轉換:將字串轉換成數字、全部大寫或全部小寫,例如 strupr(char *str) 函

數會將 str 字串轉換成全部大寫,strlwr(char *str) 函數會將 str 字串轉換成全

部小寫。除了 strupr()、strlwr() 函數之外,C 語言還提供了其它形式的字串

轉換函數,例如 atoi()、atol()、atof()…。

字串搜尋:在一個字串內搜尋子字串,例如 strstr(char *str1, char *str2) 函數會傳

回一個指標,指向 str2 字串第一次出現在 str1 字串內的位置,若 str1 字串不包

含 str2 字串,則傳回 NULL;strchr(char *str, int c) 函數會傳回一個指標,指向 c

字元第一次出現在 str 字串內的位置,若 str 字串不包含 c 字元,則傳回 NULL。

範例 2.15:[字串長度] 撰寫一個函數模擬 C語言的 strlen(),以計算字串包含幾個字元。

解答:

int StrLength(char str[])

{

int i = 0;

while(str[i] != '\0') i++;

return i;

}

範例 2.16:[字串複製 ] 撰寫一個函數模擬 C 語言的 strcpy(),以複製字串。

解答:

StrCopy(char str1[], char str2[])

{

int i = 0;

while(str2[i] != '\0'){

str1[i] = str2[i];

i++;

}

str1[i] = '\0';

}

Page 37: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-30

範例 2.17:[模式比對 (pattern matching)] 撰寫一個函數在 str 字串內比對另一

個 pat 字串,若 pat 字串在 str 字串內,就傳回其起始位置,否則傳回 -1。

解答: 在開始撰寫函數之前,我們先來想想如何在 str 字串內比對另一個 pat 字

串,最直接的就是使用暴力法,從 str 字串和 pat 字串的第一個字元開始

比對,若相等,就繼續往下比對,當 pat 字串的每個字元都比對過且相等

時,表示成功 (傳回 str 字串的比對起點)。若在比對 pat 字串的中途有任

何一個字元不相等,就回到 str 字串比對起點的下一個字元,然後繼續和

pat 字串的第一個字元比對,若 str 字串先用完,表示失敗 (傳回 -1)。

這個方法的原理雖然簡單,但效率卻不太好,假設 str 字串和 pat 字串的

長度分別為 n 與 m,在最差情況下,當 pat 字串每次都是幾乎比對到最後

一個字元時才發現不相等,此時,已經比對將近 m 次,而在比對的過程

中,str 字串的比對原點最多移動 n - m + 1 次,故最差情況的時間複雜度

為 m x (n - m + 1) = O(m x n)(假設 n > m)。

我們可以試著改善這個方法的效率,也就是先比對 pat 字串的最後一個字

元,若相等,再從 pat 字串的第一個字元開始比對,如此一來,就可以避

免比對到最後才發現不相等,徒然浪費時間,不過,這也只是有助於改

善平均情況,對於最差情況就沒有分別了。<\Ch02\ex2_17.c> int StrMatch(char str[], char pat[])

{

int i, j;

int start = 0; /*指向 str字串的比對起點*/

int end = strlen(str) - 1; /*指向 str字串的最後一個字元*/

int endp = strlen(pat) - 1; /*指向 pat字串的最後一個字元*/

int endmatch = endp; /*指向 str字串內與 pat字串的最後一個字元做比對的字元*/

for(i = 0; endmatch <= end; endmatch++, start++){

if (str[endmatch] == pat[endp])

for(j = 0, i = start; j < endp && str[i] == pat[j]; i++, j++);

if (j == endp) return start; /*表示成功*/

}

return -1; /*表示失敗*/

}

Page 38: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-31

陣列 000222

我們可以使用 str[] = "yxyxxxyyxxx" 和 pat[] = "yyx" 來模擬比對的過程:

y y x \0pat[] =

y x y xstr[] = x x y y x x x \0

j endp

start end match end

y x y x x x y y x x x \0

start end match end

no match

y x y x x x y y x x x \0

start end match end

no match

y x y x x x y y x x x \0

start end match end

no match

y x y x x x y y x x x \0

start end match end

no match

y x y x x x y y x x x \0

start end match end

no match

y x y x x x y y x x x \0

start end match end

no match

match

Page 39: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-32

2-6 結構

我們在前面有提過,多數程式語言 (包括 C 語言在內) 會限制陣列的元素必須是

相同的資料型別,也就是同質陣列 (homogeneous array),若要將不同資料型別的

元素放在一起,則可以改用結構 (structure),而且結構裡面的成員還可以是其它

結構,也就是巢狀結構 (nested structure)。

例如下面的敘述是先宣告一個名稱為 DATE 的結構,裡面有 year、month、day 三

個成員,分別代表日期的年、月、日,接著又宣告一個名稱為 PERSON 的結構,

裡面有 name、birthday 兩個成員,分別代表人的姓名、生日,其中 birthday 成員

的型別為 DATE 結構,所以 PERSON 是一個巢狀結構:

typedef struct{

int year;

int month;

int day;

}DATE; typedef struct{

char name[50];

DATE birthday;

}PERSON;

要注意的是當我們宣告結構時,只是建立結構的特性,並沒有真的配置記憶體

空間給它,若要配置記憶體給它,必須透過類似如下的宣告敘述:

PERSON employee;

至於要如何存取結構的成員,則可以使用 . 運算子,例如下面的敘述是將

employee 的姓名設定為 "Mary Wang",生日設定為 1995 年 2 月 14 日:

employee.name = "Mary Wang";

employee.birthday.year = 1995;

employee.birthday.month = 2;

employee.birthday.day = 14;

Page 40: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-33

陣列 000222

陣列 (array) 和變數 (variable) 一樣是用來存放資料,不同的是陣列雖然只有一個名稱,卻能存放多個資料,當陣列最多能夠存放 n 個元

素時,表示它的長度 (length) 為 n,而且除了一維陣列 (one-dimension

array),多數程式語言亦支援多維陣列 (multi-dimension array)。

一維陣列常見的運算有建立、讀取、寫入、插入、刪除、複製、搜尋、走訪等。

二維陣列常見的運算有矩陣轉置、矩陣相加、矩陣相乘等。

一維陣列 A[upper0] 的定址方式:假設第一個元素 A[0] 的位址為 α,則元素 A[i] 的位址為 α + i x length (每個元素的大小為 length)。

二維陣列 A[upper0][upper1] 的定址方式:假設第一個元素 A[0][0] 的位址為 α,則元素 A[i][j] 的位址為 α + i x upper1 + j (此處遵循 C 語言會自動計算元素大小的慣例,所以沒有乘上元素大小)。

三維陣列 A[upper0][upper1][upper2] 的定址方式:假設第一個元素

A[0][0][0] 的位址為 α,則元素 A[i][j][k] 的位址為 α + i x upper1 x upper2 + j x upper2 + k (此處遵循 C 語言會自動計算元素大小的慣例,

所以沒有乘上元素大小)。

n 維陣列 A[upper0][upper1]…[uppern-1] 的定址方式:假設第一個元素A[0][0]…[0] 的位址為 α,則元素 A[i0][i1][i2]…[in-1] 的位址為:

陣列本身不僅是資料結構,更可以用來實作其它抽象資料型別 (ADT),包括多項式、矩陣、字串、串列、堆疊、佇列、樹、圖形…。

對 C 語言來說,字串 (string) 是以空字元 '\0' 結尾的一串字元,因此是以字元陣列的形式來表示字串。

Page 41: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-34

1. 簡單說明陣列和變數有何不同?

2. 簡單說明何謂二維陣列並舉例說明其用途。

3. 假設以 C 語言宣告一個四維陣列 int A[5][4][3][2];,試問,該陣列包含多少個元素?

4. 假設以 C 語言宣告一個一維陣列 int A[8] = {10, 20, 30, 40, 50, 60, 70, 80};,已知 A[0] 的位址為 100 且 sizeif(int) 等於 2,試問,A[3] 為何?&A[3] 為

何?*(A + 2) 為何?

5. 假設以 C 語言宣告一個二維陣列 int A[4][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};,已知 A[1][1] 的位址 200 且 sizeif(int) 等於 2,試問,A[2][2] 為何?

&A[2][2] 為何?&A[0][0] 為何?

6. 假設以 C 語言宣告一個三維陣列 float A[8][7][6];,已知 A[3][2][1] 的位址為 300 且 sizeof(float) 等於 4,試問,A[5][4][3] 的位址為何?

7. 承題 6.,但定址方式改成以行為主,則 A[5][4][3] 的位址為何?

8. 假設以一維陣列存放 n 個整數,試問,當我們想印出陣列的最後一個元

素時,其時間複雜度為下列何者?

A. O(1) B. O(n) C. O(n2) D. O(log2n)

9. 在第 2-4-1 節介紹的前兩種方式中,何者用來存放多項式 5X10 - 3X8 + 7 較節省記憶體?為什麼?

10. 以第 2-4-1 節定義的 Polynomial 結構存放多項式 A(X) = 5X10 - 3X8 + 7、B(X) = 3X10 + 6X9 + 3X8 - 2X2和 C(X) = A(X) + B(X),然後根據如下形式寫出這三

個 Polynomial 結構的值。

count ?

[0] [1] [2] …

coef exp coef exp coef exp coef exp

terms

? ? ? ? ? ? … …

Page 42: 第 1 章 導論 - gotop.com.t · 2-2 2-1 認識陣列 對於多數學習過程式語言的人來說,陣列 (array) 並不是個陌生的名詞,甚至您 可能已經撰寫過無數個使用到陣列的程式,有關陣列的語法是再熟悉也不過,

2-35

陣列 000222

11. 以第 2-4-2 節定義的定義的 SparseTerm 結構存放如下稀疏矩陣的非零項,然後寫出各個非零項的值。

12. [字串比較 ] 撰寫一個函數模擬 C 語言的 strcmp(),以比較字串。

13. 在範例 2.14 的下三角矩陣中,我們是採以列為主的定址方式,若改採以行為主,則 C[i][j] 是存放在陣列 D 的哪個位置?

14. 在第 2-26 頁隨堂練習的上三角矩陣中,我們是採以列為主的定址方式,若改採以行為主,則 A[i][j] 是存放在陣列 B 的哪個位置?

15. [三對角線矩陣 ] 當我們使用二維陣列來存放如下圖的三對角線矩陣 (tridiagonal matrix) 時,往往會浪費許多記憶體,因為它的對角線上下方

均為 0,此時,可以改用一維陣列來存放非零項,假設以一維陣列 B 來

存放 n×n 的三對角線矩陣 A,即 A[0][0] 存放在 B[0]、A[0][1] 存放在 B[1]、

A[1][0] 存放在 B [2]、A[1][1] 存放在 B [3]、A[1][2] 存放在 B [4]、A[2][1]

存放在 B [5]…餘此類推,試問,陣列 B 的長度為何?又 A[i][j] 是存放在陣列 B 的哪個位置?