Upload
others
View
9
Download
0
Embed Size (px)
Citation preview
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
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
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
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
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
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
222CHAPTER
000222 CCCHHHAAAPPPTTTEEERRR
陣陣陣列列列
2-1 認識陣列 2-4 陣列的應用
2-2 陣列的運算 2-5 字串
2-3 陣列的定址方式 2-6 結構
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 個元素。
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
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]
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,即 。
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]
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
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*/
}
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 =
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。
例如:
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 行 (兩個向量的內積):
例如:
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];
}
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];
隨隨隨堂堂堂練練練習習習
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]
記憶體
起始位址為α
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
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] 的位址為:
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 的三維陣列中有幾個元素會被放在相同的位址?
隨隨隨堂堂堂練練練習習習
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
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
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;
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++;
}
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++;
}
}
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
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;
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]。
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 的哪個位置?
隨隨隨堂堂堂練練練習習習
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
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()…。
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';
}
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; /*表示失敗*/
}
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
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;
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' 結尾的一串字元,因此是以字元陣列的形式來表示字串。
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
? ? ? ? ? ? … …
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 的哪個位置?
零
零