Transcript ch02線性串列
親愛的老師您好 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔, 特別摘錄重點提供給您授課專用。 說明: 1、本教具為非賣品,不得作為商業之用。 2、本教具僅授權使用原著作為授課教材之教師作為教學或研究等學術用途。 3、本教具未授權提供學生任何拷貝、影印、引用、翻印等行為。 4、教師若需申請網站或內容授權,可透過您的博碩業務協助處理,謝謝。 博碩文化: 總公司:台北縣汐止市新台五路一段94號6樓A棟 電話:(02) 2696-2869 分機 313 傳真:(02) 2696-2867 網址:www.drmaster.com.tw 客服信箱:[email protected] 出書提案信箱 [email protected] 資料結構 請老師填入姓名主講 課本:圖解資料結構 博碩文化出版發行 第二章 線性串列 課前指引 「線性串列」 (Linear List)是數學應用在電腦科學中一種相 當簡單與基本的資料結構,簡單的說,線性串列是n個元素的有 限序列(n≧0),像是26個英文字母的字母串列:A,B,C,D,E…,Z, 就是一個線性串列,串列中的資料元素是屬於字母符號,或是1 0個阿拉伯數字的串列0,1,2,3,4,5,6,7,8,9。 章節大綱 2-1 線性串列的定義 2-2 陣列 2-3 矩陣 備註:可依進度點選小節 2-1 線性串列的定義 「線性串列」(Linear List)的定義: 有序串列可以是空集合,或者可寫成(a1,a2,a3.. .,an-1,an)。 存在唯一的第一個元素a1與存在唯一的最後一個 元素an。 除了第一個元素a1外,每一個元素都有唯一的先 行者(precessor),例如ai的先行者為ai-1。 除了最後一個元素an外,每一個元素都有唯一的 後續者(successor),例如ai+1是ai的後續者。 5 2-1 線性串列的定義 「線性串列」(Linear List)的用途(1/2) : 例如C/C++程式中的陣列或字串結構,就是一種典 型線性串列的應用。 特性是使用連續記憶空間(Contiguous Allocation)來儲存,記憶體配置是在編譯時,就 必須配置給相關的變數,容易造成記憶體的浪費。 Array_Name代表佔有 一塊連續的記憶體空 間,並擁有5筆資料的 陣列 6 2-1 線性串列的定義 「線性串列」(Linear List)的用途(2/2): 又或者如鏈結串列(Linked Lists)結構,也是在 C/C++中,多半是以指標變數型態來實作線性串列 的資料結構。 特點是串列節點的記憶體配置是在執行時才發生, 所以不需事先宣告,或稱為「動態記憶體配置」 指標變數是指內含值為指到記憶體儲存位置的一種資料型態變數 7 2-2 陣列 在程式語言中,可看作是一群相同名稱 與資料型態的集合,並且在計憶體中佔 有一塊連續的記憶體空間。 要存取陣列中的資料時,則配合索引值 (index)尋找出資料在陣列的位置。 8 2-2 陣列 陣列的五種屬性: 1.起始位址:表示陣列名稱(或陣列第一個元素) 所在記憶體中的起始位址。 2.維度(dimension):代表此陣列為幾維陣列,如 一維陣列、二維陣列、三維陣列等等。 3.索引上下限:指元素在此陣列中,記憶體所儲 存位置的上標與下標。 4.陣列元素個數:是索引上限與索引下限的差+1 。 5.陣列型態:宣告此陣列的型態,它決定陣列元 素在記憶體所佔有的大小。 9 2-2 陣列 多維陣列也必須在一維的實體記憶體中 表示,因為記憶體位置是依線性順序遞 增。通常依照不同的語言,又可區分為 1、以列為主(Row-major):一列一列來依序儲存 ,例如Java、C/C++、PASCAL語言的陣列存放方式 。 2、以行為主(Column-major):一行一行來依序儲 存,例如Fortran語言的陣列存放方式。 10 2-2 陣列 一維陣列: 例如假設A是一維陣列(One-dimension Array)名稱,如 果宣告為A(1:u1),表示A含有n個元素,其中1為下標, u1為上標。 A(1)、A(2)、A(3)、…… A(u1) α α+1*d α+2*d …… ……… α+( u1-1)*d =>Loc(A(i))= α+(i-1)*d (Loc(A(i))表示A(i)所在的住址) 不過如果一維陣列A是宣告為A(l1:u1),且l1為小於或 等於u1的任何整數,α為起始位址,d為單位空間,那 麼公式如下: Loc(A(i))=α+(i- l1)*d 11 2-2 陣列 範例 2.2.1 假設A為一個具有1000個元素的陣列,每個元素為 4個位元組的實數 ,若A[500]的位置為100016,請 問A[1000]的位址為何? 解答:本題主要是位址以16進位法表式→ →loc(A[1000])=loc(A[500])+(1000-500)×4 =4096+2000=6096 12 2-2 陣列 範例 2.2.2 有一PASCAL陣列 A:ARRAY[6..99] of REAL(假設 REAL元素大小有4),如果已知陣列A的起始位址為 500,則元素A[30]的位址為何? 解答: Loc(A[30])=Loc(A[6]+(30-6)*4=500+96=596 13 2-2 陣列 在C語言,一維陣列的語法宣告為: 資料型態 陣列名稱[陣列長度]; 資料型態: 表示該陣列存放的資料型態,可以是基本的資料型態( 如int,float,char…等),或延伸的資料型態,如結 構型態(struct)等。 陣列名稱: 命名規則與變數相同。 元素個數: 表示陣列可存放的資料個數,為一個正整數常數,且陣 列的索引值是從0開始。若是只有中括號,即沒有指定 常數值,則表示是定義不定長度的陣列(陣列的長度會 由設定初始值的個數決定)。 14 2-2 陣列 在C語言中定義如下陣列,可如下圖表示: int Score[5]; 15 #include <stdio.h> #include <stdlib.h> 2-2 陣列 int main() { int Score[5]={ 87,66,90,65,70 }; 請使用C的一維陣列來計錄5個學生的分數,並使 /* 定義整數陣列 Score[5],並設定5筆成績 */ int count, Total_Score=0; 用for迴圈列印出每筆學生成績及計算分數總合 for (count=0; count < 5; count++) /* 執行 for 迴圈讀取學生成績 */ { printf("第 %d 位學生的分數:%d\n", count+1,Score[count]); Total_Score+=Score[count]; /* 由陣列中讀取分數計算總合 */ } printf("-------------------------\n"); printf("5位學生的總分:%d\n", Total_Score); /* 輸出成績總分 */ 範例 2.2.3 system("pause"); return 0; } 16 2-2 陣列 二維陣列(Two-dimension Array) : 可視為一維陣列的延伸,都是處理相同資料型態 資料,差別只在於維度的宣告。 例如一個含有m*n個元素的二維陣列A (1:m,1:n) ,m代表列數,n代表行數,各個元素在直觀平面 上的排列方式如下矩陣,A[4][4]陣列中各個元素 在直觀平面上的排列方式如下 17 2-2 陣列 依照不同的語言,可區分為兩種方式: 以列為主(Row-major) 以行為主(Column-major) 18 2-2 陣列 以列為主(Row-major): 存放順序為a11,a12,...a1n,a21,a22,...,..amn,假設α 為陣列A在記憶體中起始位址,d為單位空間,那 麼陣列元素aij與記憶體位址有下列關係: Loc(aij)= α+n* (i-1)*d+(j-1)*d 19 2-2 陣列 以行為主(Column-major): 存放順序為a11,a21,...am1,a12,a22,...,..amn,假設α 為陣列A在記憶體中起始位址,d為單位空間,那 麼陣列元素aij與記憶體位址有下列關係: Loc(aij)= α+(i-1)*d+m*(j-1)*d 20 2-2 陣列 宣告陣列A(1:2,1:4),表示法如下: 21 2-2 陣列 範例 2.2.4 現有一二維陣列A,有3*5個元素,陣列的起始位 址A(1,1)是100,以列為主(Row-major)排列,每 個元素佔2 bytes,請問A(2,3)的位址? 解答:代入公式,Loc(A92,30=100+(2-1)*5*2+(3-1)*2=114 22 2-2 陣列 範例 2.2.5 二維陣列A[1:5,1:6],如果以column-major存放, 則A(4,5)排在此陣列的第幾個位置?(α=0,d=1)? 解答:Loc(A(4,5))=0+(4-1)*5*1+(5-1)*1=19(下一個), 所以A(4,5)在第20個位置 23 2-2 陣列 範例 2.2.6 陣列(Array)是以PASCAL語言來宣告,每個陣列元 素佔用4個單位的記憶體。若起始位址是255,在 下列宣告中,所列元素存放位置為何? VarA=array[-55…1,1…55],求A[1,12]之位址。 VarA=array[5…20,-10…40],求A[5,-5]之位址。 解答: 1:先求得陣列中的實際列數及行數。 1-(-55)+1=57…列數 ;55-1+1=55...行數 由於PASCAL語言是以列為主的語言,可代入以下計算公式中: 255+55*4*(1-(-55))+(12-1)*4=12619 2:同樣是先求得陣列中的實際列數及行數。 20-5+1=16…列數; 40-(-10)+1=51...行數 255+4*51*((5-5)+4*(-5-(-10))=275 24 2-2 陣列 範例 2.2.7 A(-3:5,-4:2)的起始位址A(-3,-4)=1200,以rowmajor排列,每個元素佔1bytes,請問 Loc(A(1,1))=? 解答: 假設A陣列以row-major排列,且α=Loc(A(-3,-4))=100 m=5-(-3)+1=9(列)、n=2-(-4)=1=7(行), A(1,1)=1200+1*7*(1-(-3))+1*(1-(-4))=1233 25 #include <stdio.h> #include <stdlib.h> int main() { int i,j,sum,max=0,no=1; int sale[3][6]={{112,76,95,120,98,68}, {90,120,88,112,108,120}, {108,99,126,90,76,98}}; for(i=0;i<3;i++) { sum=0; for(j=0;j<6;j++) sum+=sale[i][j];/* 加上每月的業績金額 */ printf("銷售員%d的前半年銷售總金額為 %d\n",i+1,sum); printf("------------------------------------\n"); } 2-2 陣列 範例 2.2.8 以下是數位新知公司三個業務代表2008年前六個 月每人的業績,請使用二維陣列設計一C程式,計 算以下結果: for(i=0;i<6;i++) { sum=0; for(j=0;j<3;j++) sum+=sale[j][i];/* 加上每月的業績金額 */ printf("所有銷售員%d月的銷售總金額為 %d\n",i+1,sum); printf("=====================================\n"); (1)每個業務代表的前半年業績總額。 (2)1~6月每個月這三個業務代表的總業績。 } system("pause"); return 0; } 26 解答: #include <stdio.h> #include <stdlib.h> int main() { int arr[2][2]; int sum; printf("|a1 b1|\n"); printf("|a2 b2|\n"); printf("請輸入a1:"); scanf("%d",&arr[0][0]); printf("請輸入b1:"); scanf("%d",&arr[0][1]); printf("請輸入a2:"); scanf("%d",&arr[1][0]); printf("請輸入b2:"); scanf("%d",&arr[1][1]); sum = arr[0][0]*arr[1][1]-arr[0][1]*arr[1][0];/* 求二階行列式的值 */ printf("|%d %d|\n",arr[0][0],arr[0][1]); printf("|%d %d|\n",arr[1][0],arr[1][1]); printf("sum=%d\n",sum); system("pause"); return 0; } 2-2 陣列 範例 2.2.9 利用二維陣列的方式來撰寫一個求二階行列式的 範例。二階行列式的計算公式為:a1*b2-a2*b1。 27 2-2 陣列 多維陣列(1/3) 在程式語言中,只要記憶體大小許可時,都可以 宣告成更多維陣列來存取資料,通常凡是二維以 上的陣列都可以稱作多維陣列。 三維陣列的表示法和二維陣列一樣,皆可視為是 一維陣列的延伸,如果陣列A宣告為 A(1:u1,1:u2,1:u3),表示A為一個含有u1*u2*u3元 素的三維陣列,可以看作是一個立方體,如下圖 28 2-2 陣列 多維陣列(2/3) 以列為主(Row-major) 在想像轉換公式時,是要計算A(i,j,k)看看它是在一直 線排列的第幾個各位。 可以得到aijk元素的以下位址計算公式: Loc(A(i,j,k))=α+(i-1)u2u3d+(j-1)u3d+(k-1)d 29 2-2 陣列 多維陣列(3/3) 以行為主(Row-major) 也可以直觀地將陣列A視為u3個u2*u1的二為陣列,再將 每個二維陣列視為有u2個一維陣列,每一陣列含有u1個 元素。每個元素有d單位空間,且α為起始位址。 可以得到aijk元素的以下位址計算公式: Loc( A(aijk)=α+(k-1)u2u1d+(j-1)u1d+(i-l)d 30 2-2 陣列 範例 2.2.10 假設有以列為主排列的程式語言,宣告 A(1:3,1:4,1:5)陣列,且Loc(A(1,1,1))=100,請 求出Loc(A(1,2,3))=? 解答:直接代入公式 Loc(A(1,2,3))=100+(1-1)*4*5*1+(2-1)*5*1+(3-1)*1=107 31 2-2 陣列 範例 2.2.11 A(6,4,2)是以行為主方式排列,若α=300,且d=1, 求A(4,4,1)的位址? 解答:這題是以列為主(Row-Major),直接代入公式即可: Loc(A(4,4,1))=300+(1-1)*6*4+(4-1)*2+(6-1)=300+6+5=311 32 2-2 陣列 範例 2.2.12 假設有一三維陣列宣告為A(1:3,1:4,1:5), A(1,1,1)=300,且d=1,試問以行為主的排列方式 下,求出A(2,2,3)的所在位址。 解答: Loc(A(1,2,3))=300+(3-1)*3*4*1+(2-1)*3*1+(2-1)=328 33 2-2 陣列 範例 2.2.13 有一個三維陣列A(-3:2,-2:3,0:4),以Row-major 方式排列,陣列之起始位址是1118,試求 Loc(A(1,3,3))=?(d=1) 解答: 假設A為u1*u2*u3陣列,且以row-major方式排列 m=2-(-3)+1=6 n=3-(-2)+1=6 o=4-0+1=5 公式如下: =>Loc(A(1,3,3))=318+(1-(-3))*6*5+(3-(-2))*5+(30)=318+120+25+3=1266 34 2-2 陣列 範例 2.2.14 假設有一三維陣列宣告為A(-3:2,-2:3,0:4), A(1,1,1)=300,且d=2,試問以行為主的排列方式 下,求出A(2,2,3)的所在位址。 解答: m=2-(-3)+1=6 n=3-(-2)+1=6 o=4-0+1=5 Loc(A(1,2,3))=300+(3-0)*6*6*1+(2-(-2))*6*1+(2-(-3))*1=437 35 2-2 陣列 在C中,凡是二維以上的陣列都可以稱作 多維陣列 想要提高陣列的維度,只要在宣告陣列 時,增加中括號與索引值即可。 定義方式如下所示: 資料型態 陣列名稱[元素個數] [元素個數] [元素個數]……. [元素個數]; 範例: float No[2][2][2]; 36 #include <stdio.h> #include <stdlib.h> int main() { int i,j,k,sum=0; 2-2 陣列 範例 2.2.15 int arr[4][3][3]={{{1,-2,3},{4,5,-6},{8,9,2}}, {{7,-8,9},{10,11,12},{0.8,3,2}}, {{-13,14,15},{16,17,18},{3,6,7}}, {{19,20,21},{-22,23,24},(-6,9,12)}};/* 宣告並設定陣列元素值 */ for(i=0;i<4;i++) { A[4][3][3]={{{1,-2,3},{4,5,-6},{8,9,2}}, for(j=0;j<3;j++) {{7,-8,9},{10,11,12},{0.8,3,2}}, { {{-13,14,15},{16,17,18},{3,6,7}}, for(k=0;k<3;k++) {{19,20,21},{-22,23,24},(6-,9,12)}}; { sum+=arr[i][j][k]; if (arr[i][j][k]<0) arr[i][j][k]=0;/* 元素值於為負數,則歸零 */ printf("%d\t",arr[i][j][k]); } printf("\n"); } printf("\n"); } printf("---------------------------\n"); printf("原陣列的所有元素值總和=%d\n",sum); printf("---------------------------\n"); system("pause"); return 0; 假設一個三維陣列元素內容如下: 請設計一C程式來計算此陣列中的每個元素值總和,並將 值為負數的元素值都換為0,再輸出新陣列的所有內容。 } 37 #include <stdio.h> #include <stdlib.h> int main() { int num[2][3][3]={{{33,45,67}, {23,71,56}, {55,38,66}}, {{21,9,15 }, {38,69,18}, {90,101,89}}};//宣告三維陣列 int num[2][3][3]={{{33,45,67}, int i,j,k,min=num[0][0][0];//設定main為num陣列的第一個元素 {23,71,56}, for(i=0;i<2;i++) {55,38,66}}, for(j=0;j<3;j++) {{21,9,15 }, for(k=0;k<3;k++) if(min>=num[i][j][k]) {38,69,18}, min=num[i][j][k]; //利用三層迴圈找出最小值 {90,101,89}}} printf("最小值= %d\n",min); system("pause"); 請設計一支C程式,利用三層巢狀迴圈來找出此2x3x3三 return 0; 維陣列中所儲存數值中的最小值。 } 2-2 陣列 範例 2.2.16 假設一個三維陣列元素內容如下: 38 2-2 陣列 結構陣列(1/6) 結構與陣列的作用同樣是用來建立資料集合 以C現有的資料型態作為基礎,允許使用者建立自 訂資料型態,又稱為衍生資料型態(derived data type)。 簡單來說,就是將一組有相關性卻擁有不同資料 型態的資料組成一組新的資料型態。 結構的架構必須具有結構名稱與結構項目,而且 必須使用關鍵字struct來建立。 39 2-2 陣列 結構陣列(2/6) 結構的基本宣告方式如左下所示: struct 結構名稱 { 資料型態 結構成員1; 資料型態 結構成員2; ...... 範例 struct student { char name[10]; int score; int ID; }; }; 在結構定義中可以使用基本的變數、陣列、指標,甚至 是其它結構成員等。 注意在定義之後的分號不可省略 右上圖為一個結構定義例子,定義學生的姓名與成績 40 2-2 陣列 結構陣列(3/6) 定義了結構之後,便可直接使用它來建立結構物件 結構定義本身就像是個建構物件的藍圖或模子 結構物件則是根據這個藍圖製造出來的成品或模型 每個所建立的結構物件都擁有相同的結構成員 一個宣告建立結構物件的例子如下所示: struct student s1, s2; 也可以在定義結構的同時宣告建立結構變數,如下: struct student { char name[10]; int score; int ID; } s1, s2; 41 2-2 陣列 結構陣列(4/6) 建立結構物件之後,我們可以使用英文句號.來存 取結構成員。 這個句號通常稱之為「點運算子」(dot operator) 只要在結構變數後加上成員運算子"."與結構成員 名稱,就可以直接存取該筆資料: 結構變數.項目成員名稱; 例如我們可以如下設定結構成員 strcpy(s1.name, "Justin"); s1.score = 90; s1.ID=10001; 42 2-2 陣列 結構陣列(5/6) 結構陣列則是將衍生型資料型態(Structure data type)與陣列的共同應用。 結構則可以集合不同的資料型態,如果同時要記錄 多筆相同結構資料,還是得宣告一個結構陣列型態 。宣告方式如下: struct 結構型態名稱 結構陣列名稱[元素個素]; 43 2-2 陣列 結構陣列(6/6) 下面程式碼片段將建立具有五個元素的結構陣列: struct student { char name[10]; int math; int english; }; struct student class1[5]; 至於要存取的成員,在陣列後方加上“[索引值]” 存取該元素即可,例如: 結構陣列名稱[索引值].陣列成員名稱 44 2-2 陣列 字元陣列(1/3) 在C語言中,並沒有稱為字串(string)的基本資料 型態。 如果要在C程式中儲存字串,就必需使用字元陣列 來表示。 ‘a’是一個字元常數,是以單引號( ‘ )包括起來。 “a”則是一個字串常數,是以雙引號( “ )包括起來。 兩者的差別在於字串的結束處會多安排1個位元組的空間 來存放’\0‘字元(Null字元,ASCII碼為0),作為字串 結束時的符號。 45 2-2 陣列 字元陣列(2/3) C字串宣告方式有兩種: 方式1:char 字串變數[字串長度]="初始字串"; 方式2:char 字串變數[字串長度]={'字元1', '字元2', ...... ,'字元n', '\0'}; 46 2-2 陣列 字元陣列(3/3) 以四種宣告方式說明: char Str_1[6]="Hello"; char Str_2[6]={ 'H', 'e', 'l', 'l', 'o' , '\0'}; char Str_3[ ]="Hello"; char Str_4[ ]={ 'H', 'e', 'l', 'l', 'o', '!' }; Str_4並不是字串常數,因為最後字元並不是'\0'字元, 輸出時會出現奇怪的符號。 47 #include<stdio.h> #include<stdlib.h> 2-2 陣列 int main() { int length;/*用作計算字串的長度*/ 在字串的處理中,是字元陣列的應用。通常會需 char str[30]; 範例 2.2.17 要知道字串的長度,請設計一C程式,並利用 printf("請輸入字串:"); while迴圈逐一讀取字元,並計算此輸入字串的長 /*輸入字串*/ 度。如輸入"changeable",會輸出長度10。 gets(str); printf("輸入的字串為:%s\n",str); length=0; while (str[length]!='\0') length++; printf("此字串有%d個字元\n",length); system("pause"); return 0; } 48 #include <stdio.h> #include <stdlib.h> 2-2 陣列 int main() { int i=0; char str[50]; 範例 2.2.18 請設計一C程式,將所輸入字串中的大寫字母轉為 printf("請輸入一個字串:"); 小寫,小寫字母轉為大寫。如輸入"aPPle",會輸 gets(str); 出"AppLE"。 while(str[i]!='\0') { if(str[i]>=65 && str[i]<=90) str[i]+=32;/* 大寫換小寫 */ else if (str[i]>=97 && str[i]<=122) str[i]-=32;/* 小寫換大寫 */ i++; } printf("大小寫轉換後的新字串=%s\n",str); system("pause"); return 0; } 49 #include <stdio.h> #include <stdlib.h> 2-2 陣列 int main() { char arr2[50]; int i,sum; 請設計一C程式,將使用者輸入的原始字串資料反 printf("請輸入一個字串:"); 向排列輸出的程式。如"changeable",會輸出 gets(arr2); 範例 2.2.19 "elbaegnahc"。 for (i=0;i<50;i++) { if (arr2[i]=='\0') break; //如果是使用者輸入字串的結尾就中斷迴圈 sum=i;//紀錄空字元前一個字元的索引 } for (i=sum;i>=0;i--) //將使用者輸入字串反向輸出 printf("%c",arr2[i]); printf("\n"); system("pause"); return 0; } 50 2-2 陣列 字串陣列(1/2) 字串是以一維的字元陣列來儲存,如果有許多關係 相近的字串集合時,就稱為字串陣列。 屬於二維字元陣列的應用。 字串陣列使用時也必須事先宣告。 51 2-2 陣列 字串陣列(2/2) C中的字串陣列宣告方式如下: char 字串陣列名稱[字串數][字元數]; 字串數是表示字串的個數 而字元數是表示每個字串的最大自可存放字元數。 可以在宣告時就設定初值,不過要記得每個字串元素都必須包含於 雙引號之內,例如。 char 字串陣列名稱[字串數][字元數]={ "字串常數1", "字串常數2", "字串常數3"…}; char Name[5][10]={"John", "Mary", "Wilson", 範例 "Candy", "Allen" }; 52 #include <stdio.h> #include <stdlib.h> 2-2 陣列 int main() { char Name[5][10]={"John", 範例 2.2.20 "Mary", 假設一個字串陣列元素內容如下: "Wilson", "Candy", "Mary", "Allen"};/* 字串陣列的宣告 */ char Name[5][10]={"John", int i; "Wilson", "Candy", for(i=0;i<5;i++) "Allen"};// 字串陣列的宣告 printf("Name[%d]=%s\n",i,Name[i]); /* 印出字串陣列內容 */ 請設計一C程式來說明Name字串陣列的宣告與輸出 方式 printf("\n"); system("pause"); return 0; } 53 2-2 陣列 指標陣列(1/2) 結合了靜態資料結構與動態資料結構的應用,十分 有趣。 每個指標陣列中的元素都是一個指標變數,而元素 值則為指向其它變數的位址值。 54 2-2 陣列 字串陣列(2/2) 一維指標陣列的宣告格式: :資料型態 *陣列名稱[元素名稱]; 以下是宣告一個名稱為p的整數指標陣列: 每個元素(p[i])皆可指向一整數值。 另外一個則是宣告一個名稱為ptr的浮點數指標陣列: int *p[3]; float *ptr[4]; 55 #include <stdio.h> #include <stdlib.h> int main() { char *name[4] = { "Justinian", "Momo", "Becky", "Bush" };/* 一維指標陣列 */ char name1[4][10] = { "Justinian", "Momo", "Becky", "Bush" };/* 二維字串陣列 int i; printf("---------- 一維指標陣列儲存方式 -------------- \n"); for ( i = 0; i < 4; i++ ) { char name[4][10] = { "Justinian", "Momo", "Becky", "Bush" }; printf( "name[%d] = \"%s\"\t",i,name[i] ); printf( "所佔位址:%p \n", name[i]); 列印name[i]出所佔位址 */ char *name[4] = { "Justinian", "Momo",/* "Becky", "Bush" }; } printf("------------ 二維字串陣列儲存方式-------------- \n"); for ( i = 0; i < 4; i++ ) { printf( "name1[%d] = \"%s\"\t",i,name1[i] ); printf( "所佔位址:%p \n",&name1[i][0]);/* 列印name1[i]出所佔位址 */ } system("pause"); return 0; } 2-2 陣列 範例 2.2.21 */ 假設兩陣列的內容如下: 請設計一C程式來說明一維指標陣列與二維字串陣 列來儲存字串的不同之處。 56 2-3 矩陣 從數學的角度來看,對於m×n矩陣(Matrix) 的形式,可以描述一個電腦中A(m,n)二維 陣列 許多矩陣的運算與應用,都可以使用電腦中的二維 陣列解決,例如討論到兩個矩陣的相加、相乘,或 是某些稀疏矩陣(Sparse Matrix)、轉置矩陣(At) 等等 57 2-3 矩陣 矩陣的運算 從數學的角度來看,矩陣的運算方式可以涵蓋 加法 乘積 轉置 58 2-3 矩陣 矩陣的運算 轉置矩陣 「轉置矩陣」(At)就是把原矩陣的行座標元素與列座標 元素相互調換,假設At為A的轉置矩陣,則有 At[j,i]=A[i,j]如下所示: m*n矩陣的轉置矩陣的C演算法: for(i=0;i<m;i++) for(j=0;j<n;j++) arrB[i][j]=arrA[j][i]; 59 #include <stdio.h> #include <stdlib.h> int main() { int arrB[4][4],i,j; int arrA[4][4]={ {1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16} }; printf("[請輸入矩陣內容]\n"); for(i=0;i<4;i++) { for(j=0;j<4;j++) { printf("%d\t",arrA[i][j]); } printf("\n"); } /*進行矩陣轉置的動作*/ for(i=0;i<4;i++) for(j=0;j<4;j++) arrB[i][j]=arrA[j][i]; printf("[轉置矩陣內容為]\n"); for(i=0;i<4;i++) { for(j=0;j<4;j++) { printf("%d\t",arrB[i][j]); } printf("\n");/* 列印轉置矩陣內容 */ } system("pause"); 2-3 矩陣 範例 2.3.1 請設計一C程式來實作一4*4二維陣列的轉置矩陣 60 2-3 矩陣 矩陣的運算 矩陣的加法 前題是相加的兩矩陣列數與行數都必須相等,而相加後 矩陣的列數與行數也是相同。例如Amxn+Bmxn=Cmxn。 底下為矩陣相加的例子: 2個m*n矩陣的矩陣相加C演算法: for(i=0;i<3;i++) for(j=0;j<3;j++) C[i][j]=A[i][j]+B[i][j];/* 矩陣C=矩陣A+矩陣B */ 61 #include <stdio.h> #include <stdlib.h> 2-3 矩陣 int main() { int i,j; int A[3][3] = {{1,3,5},{7,9,11},{13,15,17}};/* 二維陣列的宣告 */ 請設計一C/C++程式來宣告3個二維陣列來實作2個 int B[3][3] = {{9,8,7},{6,5,4},{3,2,1}};/* 二維陣列的宣告 */ int C[3][3] = {0}; 範例 2.3.2 矩陣相加的過程,並顯示兩矩陣相加後的結果。 for(i=0;i<3;i++) for(j=0;j<3;j++) C[i][j]=A[i][j]+B[i][j];/* 矩陣C=矩陣A+矩陣B */ printf("[矩陣A和矩陣B相加的結果]\n"); /*印出A+B的內容*/ for(i=0;i<3;i++) { for(j=0;j<3;j++) printf("%d\t",C[i][j]); printf("\n"); } system("pause"); return 0; } 62 2-3 矩陣 矩陣的運算 矩陣相乘 兩個矩陣A與B的相乘,首先必須符合A為一個m*n的矩陣, B為一個n*p的矩陣,對A*B之後的結果為一個m*p的矩陣C。 m*n矩陣與n*p矩陣的矩陣相乘C演算法: for(i=0;i<3;i++) for(j=0;j<3;j++) { C[i][j]=0; for(k=0;k<2;k++) C[i][j]=C[i][j]+A[i][k]*B[k][j];/* 矩陣C=矩陣A+矩陣B */ } 63 /* [示範]:運算兩個矩陣相乘的結果 */ #include <stdio.h> #include <stdlib.h> #include <conio.h> void MatrixMultiply(int*,int*,int*,int,int,int); int main() { int *A,*B,*C; int M,N,P; int i,j; printf("請輸入矩陣A的維數(M,N): \n"); printf("M= "); scanf("%d",&M); printf("N= "); scanf("%d",&N); A = (int*)malloc(M*N*sizeof(int)); printf("[請輸入矩陣A的各個元素]\n"); for(i=0;i<M;i++) for(j=0;j<N;j++) { printf("a%d%d=",i,j); scanf("%d",&A[i*N+j]); } printf("請輸入矩陣B的維數(N,P): "); printf("\nN= "); scanf("%d",&N); printf("P= "); scanf("%d",&P); B = (int*)malloc(N*P*sizeof(int)); 2-3 矩陣 範例 2.3.3 請設計一C程式來實作下列兩個可自行輸入矩陣維 數的相乘過程,並顯示相乘後的結果。 續下頁 64 printf("[請輸入矩陣B的各個元素]\n"); for(i=0;i<N;i++) for(j=0;j<P;j++) { printf("b%d%d=",i,j); scanf("%d",&B[i*P+j]); 2-3 矩陣 } C = (int*)malloc(M*P*sizeof(int)); MatrixMultiply(A,B,C,M,N,P); printf("[AxB的結果是]\n"); for(i=0;i<M;i++) { for(j=0;j<P;j++) printf("%d\t",C[i*P+j]); printf("\n"); } system("pause"); } void MatrixMultiply(int* arrA,int* arrB,int* arrC,int M,int N,int P) { int i,j,k,Temp; if(M<=0||N<=0||P<=0) { printf("[錯誤:維數M,N,P必須大於0]\n"); return; } for(i=0;i<M;i++) for(j=0;j<P;j++) { Temp = 0; for(k=0;k<N;k++) Temp = Temp + arrA[i*N+k]*arrB[k*P+j]; arrC[i*P+j] = Temp; } } 65 2-3 矩陣 稀疏矩陣 對於抽象資料型態而言,我們希望闡述的是在電腦 應用中具備某種意義的特別概念(Concept)。 什麼是稀疏矩陣呢? 「如果一個矩陣中的大部分元素為零的話,就可以稱為 稀疏矩陣」。 典型的稀疏矩陣: 66 2-3 矩陣 稀疏矩陣 對稀疏矩陣而言,實際儲存的資料項目很少,如果 在電腦中利用傳統的二維陣列方式存放,就會十分 浪費儲存的空間。 改進記憶體空間浪費的方法: 利用三項式(3-tuple)的資料結構。我們把每一個非零項 目以(i,j,item-value)來表示。就是假如一個稀疏矩陣 有n個非零項目,那麼可以利用一個A(0:n,1:3)的二維陣 列來表示,我們稱為壓縮矩陣。 67 2-3 矩陣 稀疏矩陣 A(0,1)代表此稀疏矩陣的列數 A(0,2)代表此稀疏矩陣的行數 A(0,3)則是此稀疏矩陣非零項目的總數。 稀疏矩陣 壓縮矩陣 68 #include <stdio.h> #include <stdlib.h> int main () { int i,j,NONZERO=0; int temp=1; int Sparse[6][6]={ 15,0,0,22,0,-15,0,11,3,0,0,0, 0,0,0,-6,0,0,0,0,0,0,0,0,91,0 ,0,0,0,0,0,0,28,0,0,0};/*宣告稀疏矩陣,稀疏矩陣的所有元素設為0*/ int Compress[9][3]; /*宣告壓縮矩陣*/ printf("[稀疏矩陣的各個元素]\n"); /*印出稀疏矩陣的各個元素*/ for (i=0;i<6;i++) { for (j=0;j<6;j++) { printf("[%d]\t ",Sparse[i][j]); if (Sparse[i][j] !=0) NONZERO++; } printf("\n"); } /*開始壓縮稀疏矩陣*/ Compress[0][0] = 6; Compress[0][1] = 6; Compress[0][2] = NONZERO; 續下頁 for (i=0;i<6;i++) for (j=0;j<6;j++) if (Sparse[i][j] != 0) 2-3 矩陣 範例 2.3.4 請設計一C程式來利用3項式(3-tuple)資料結構, 並壓縮6*6稀疏矩陣,以達到減少記憶體不必要的 浪費。 69 { 2-3 矩陣 Compress[temp][0]=i; Compress[temp][1]=j; Compress[temp][2]=Sparse[i][j]; temp++; } printf("[稀疏矩陣壓縮後的內容]\n"); /*印出壓縮矩陣的各個元素*/ for (i=0;i<NONZERO+1;i++) { for (j=0;j<3;j++) printf("[%d] ",Compress[i][j]); printf("\n"); } system("pause"); } 70 2-3 矩陣 上三角形矩陣(Upper Triangular Matrix) 一種對角線以下元素皆為0的n*n矩陣,可分為 右上三角形矩陣(Right Upper Triangular Matrix) 左上三角形矩陣(Left Upper Triangular Matrix)。 由於上三角形矩陣仍有許多元素為0,為了避免浪 費空間,可以把三角形矩陣的二維模式,儲存在一 維陣列中。 71 2-3 矩陣 右上三角形矩陣矩陣 即對nxn的矩陣A,假如i>j,那麼A(i,j)=0,如下 圖所示: 由於此二維矩陣的非零項目可依序對映成一維矩陣,且 需要一個一維陣列B(1: )來儲存。對映方式也可區分為 以列為主(Row-major) 以行為主(Column-major)兩種陣列記憶體配置方式。 72 2-3 矩陣 右上三角形矩陣矩陣 以列為主(Row-major) k=n*(i-1)- i * (i 1) 2 +j 以列為主(Row-major) k= j * ( j 1) 2 +i 73 2-3 矩陣 範例 2.3.5 假如有一個5x5的右上三角形矩陣A,以行為主對 映到一維陣列B,請問a23所對映B(k)的k值為何? 解答:直接代入右上三角形矩陣公式: k= j * ( j 1) 2 +I = 3 * (3 1) 2 +2=5=>對映到B(5) 74 /* [示範]:上三角矩陣 */ #include <stdio.h> #define ARRAY_SIZE 5 /* 矩陣的維數大小 */ int getValue(int ,int); int A[ARRAY_SIZE][ARRAY_SIZE]={ /*上三角矩陣的內容 */ {7, 8, 12, 21, 9}, {0, 5, 14, 17, 6}, {0, 0, 7, 23, 24}, {0, 0, 0, 32, 19}, {0, 0, 0, 0, 8}}; /* 一維陣列的陣列宣告 */ int B[ARRAY_SIZE*(1+ARRAY_SIZE)/2]; int main() { int i=0,j=0; int index; printf("==========================================\n"); printf("上三角形矩陣:\n"); for ( i = 0 ; i < ARRAY_SIZE ; i++ ) { for ( j = 0 ; j < ARRAY_SIZE ; j++ ) printf("\t%d",A[i][j]); printf("\n"); } /* 將右上三角矩陣壓縮為一維陣列 */ index=0; 2-3 矩陣 範例 2.3.6 請設計一C程式,將右上三角形矩陣壓縮為一維陣 列 續下頁 75 for ( i = 0 ; i < ARRAY_SIZE ; i++ ) { for ( j = 0 ; j < ARRAY_SIZE ; j++ ) { if(A[i][j]!=0) B[index++]=A[i][j]; 2-3 矩陣 } } printf("==========================================\n"); printf("以一維的方式表示:\n"); printf("\t["); for ( i = 0 ; i < ARRAY_SIZE ; i++ ) { for ( j = i ; j < ARRAY_SIZE ; j++ ) printf(" %d",getValue(i,j)); } printf(" ]"); printf("\n"); system("pause"); } int getValue(int i, int j) { int index = ARRAY_SIZE*i - i*(i+1)/2 + j; return B[index]; } 76 2-3 矩陣 左上三角形矩陣矩陣 即對nxn的矩陣A,假如i>n-j+1時,A(i,j)=0,如 下圖所示: 與右上三角形矩陣相同,對應方式也分為以列為主 及以行為主兩種陣列記憶配體置方式。 77 2-3 矩陣 左上三角形矩陣矩陣 以列為主(Row-major) k=n*(i-1)=n*(i-1)- 以列為主(Row-major) (i 2) * (( i 2) 1) +j 2 (i 2) * (i 1) +j 2 k= n*(j-1)- ( j 2) * ( j 1) +i 2 78 2-3 矩陣 範例 2.3.7 假如有一個5*5的左上三角形矩陣,以行為主對映 到一維陣列B,請問a23所對映b(k)的k值為何? ( j 2) * ( j 1) 解答:由公式可得k=n*(j-1)+i2 =5*(3-1)+2=10+2-1=11 (3 2) * (3 1) 2 79 2-3 矩陣 左下三角形矩陣矩陣 即對nxn的矩陣A,假如i<j,那麼A(i,j)=0如下圖 所示: 同樣的,對映到一維陣列B(1: n * (2n 1) )的方式,也 可區分為以列為主及以行為主兩種陣列記憶體配置 方式。 80 2-3 矩陣 左下三角形矩陣矩陣 以列為主(Row-major) k= 以列為主(Row-major) i * (i 1) +j 2 ( j 1) * [1 ( j 1)] 2 k=n*(j-1)+i=n*(j-1)+i- j * (2j 1) 81 2-3 矩陣 範例 2.3.8 有一6x6的左下三角形矩陣,以行為主的方式對映 到一維陣列B,求元素a32所對映B(k)的大值為何? 解答:代入公式可得 j * ( j 1) 代入公式k=n*(j-1)+i- 2 =6*(2-1)+3=6+3-1=8 2 * ( 2 1) 2 82 /* [示範]:下三角矩陣 */ #include <stdio.h> #define ARRAY_SIZE 5 /* 矩陣的維數大小 */ int getValue(int ,int); int A[ARRAY_SIZE][ARRAY_SIZE]={ /*下三角矩陣的內容 */ {76, 0, 0, 0, 0}, {54, 51, 0, 0, 0}, {23, 8, 26, 0, 0}, {43, 35, 28, 18, 0}, {12, 9, 14, 35, 46}}; /* 一維陣列的陣列宣告 */ int B[ARRAY_SIZE*(1+ARRAY_SIZE)/2]; int main() { int i=0,j=0; int index; printf("==========================================\n"); printf("下三角形矩陣:\n"); for ( i = 0 ; i < ARRAY_SIZE ; i++ ) { for ( j = 0 ; j < ARRAY_SIZE ; j++ ) printf("\t%d",A[i][j]); printf("\n"); } /* 將左下三角矩陣壓縮為一維陣列 */ index=0; 2-3 矩陣 範例 2.3.9 請設計一C程式,將左下三角形矩陣壓縮為一維陣 列 83 for ( i = 0 ; i < ARRAY_SIZE ; i++ ) { for ( j = 0 ; j < ARRAY_SIZE ; j++ ) { if(A[i][j]!=0) B[index++]=A[i][j]; 2-3 矩陣 } } printf("==========================================\n"); printf("以一維的方式表示:\n"); printf("\t["); for ( i = 0 ; i < ARRAY_SIZE ; i++ ) { for ( j = i ; j < ARRAY_SIZE ; j++ ) printf(" %d",getValue(i,j)); } printf(" ]"); printf("\n"); system("pause"); } int getValue(int i, int j) { int index = ARRAY_SIZE*i-i*(i+1)/2+j; return B[index]; } 84 2-3 矩陣 右下三角形矩陣矩陣 即對nxn的矩陣A,假如i<n-j+1,那麼A(i,j)=0, 如下圖所示 同樣的,對映到一維陣列B(1: n * (n2 1) )的方式,也可 區分為以列為主與以行為主兩種陣列記憶體配置方 式 85 2-3 矩陣 右下三角形矩陣矩陣 以列為主(Row-major) k= (i 1) *[1+(i-1)]+j-(n-i) 2 i] = [i * (i 1) 2 * +j-n 2 = 以列為主(Row-major) i * (i 1) +j-n 2 k= [( j 1) * [1 ( j +i-(n-j) 1)] 2 = j * ( j 1) +i-n 2 86 2-3 矩陣 範例 2.3.10 假設有一個4x4的右下三角形矩陣,以行為主對映 到一維陣列B,求元素a32所對映B(k)的k值為何? 解答: 代入公式k= j * ( j 1) +i-n 2 = 2 * (22 1) +3-4 =2 87 解答: 2-3 矩陣 由題意得知B為左下三角形矩陣,因此不為0的個數為 n * (n 1) 2 範例 2.3.11 可將B陣列非零項目的值以列為主(Row-major)對映到一維陣列A中,且如下圖 所示: 一個低部三角陣列(Lower Triangular Array),B 是一個nxn的陣列,其中B[i,j]=0,i<j。 求B陣列中不為0的最大個數。 如何將B陣列以最經濟的方式儲存在記憶體中。 寫出在的儲存方式中,如何求得B[i,j],i>=j。 以列為主的對映方式,bij=A(k) k= i * (i 1) +j 2 88 2-3 矩陣 帶狀矩陣 一種在應用上較為特殊且稀少的矩陣。 定義就是在上三角形矩陣中,右上方的元素皆為零 ,在下三角形矩陣中,左下方的元素也為零,也就 是除了第一列與第n列有兩個元素外,其餘每列都 具有三個元素,使得中間主軸附近的值形成類似帶 狀的矩陣。如下圖所示: 89 本章結束 Q&A討論時間 90