900 likes | 1.05k Views
親愛的老師您好. 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔,特別摘錄重點提供給您授課專用。 說明: 1 、本教具為非賣品,不得作為商業之用。 2 、本教具僅授權使用原著作為授課教材之教師作為教學或研究等學術用途。 3 、本教具未授權提供學生任何拷貝、影印、引用、翻印等行為。 4 、教師若需申請網站或內容授權,可透過您的博碩業務協助處理,謝謝。. 博碩文化: 總公司:台北縣汐止市新台五路一段 94 號 6 樓 A 棟 電話: (02) 2696-2869 分機 313 傳真: (02) 2696-2867
E N D
親愛的老師您好 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔,特別摘錄重點提供給您授課專用。 說明: 1、本教具為非賣品,不得作為商業之用。 2、本教具僅授權使用原著作為授課教材之教師作為教學或研究等學術用途。 3、本教具未授權提供學生任何拷貝、影印、引用、翻印等行為。 4、教師若需申請網站或內容授權,可透過您的博碩業務協助處理,謝謝。 博碩文化: 總公司:台北縣汐止市新台五路一段94號6樓A棟 電話:(02) 2696-2869 分機 313 傳真:(02) 2696-2867 網址:www.drmaster.com.tw客服信箱:school@drmaster.com.tw 出書提案信箱 schoolbook@drmaster.com.tw
資料結構 請老師填入姓名主講 課本:圖解資料結構 博碩文化出版發行
第二章 線性串列 課前指引 「線性串列」 (Linear List)是數學應用在電腦科學中一種相當簡單與基本的資料結構,簡單的說,線性串列是n個元素的有限序列(n≧0),像是26個英文字母的字母串列:A,B,C,D,E…,Z,就是一個線性串列,串列中的資料元素是屬於字母符號,或是10個阿拉伯數字的串列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的後續者。
2-1 線性串列的定義 • 「線性串列」(Linear List)的用途(1/2) : • 例如C/C++程式中的陣列或字串結構,就是一種典型線性串列的應用。 • 特性是使用連續記憶空間(Contiguous Allocation)來儲存,記憶體配置是在編譯時,就必須配置給相關的變數,容易造成記憶體的浪費。 Array_Name代表佔有一塊連續的記憶體空間,並擁有5筆資料的陣列
2-1 線性串列的定義 • 「線性串列」(Linear List)的用途(2/2): • 又或者如鏈結串列(Linked Lists)結構,也是在C/C++中,多半是以指標變數型態來實作線性串列的資料結構。 • 特點是串列節點的記憶體配置是在執行時才發生,所以不需事先宣告,或稱為「動態記憶體配置」 指標變數是指內含值為指到記憶體儲存位置的一種資料型態變數
2-2 陣列 • 在程式語言中,可看作是一群相同名稱與資料型態的集合,並且在計憶體中佔有一塊連續的記憶體空間。 • 要存取陣列中的資料時,則配合索引值(index)尋找出資料在陣列的位置。
2-2 陣列 • 陣列的五種屬性: • 1.起始位址:表示陣列名稱(或陣列第一個元素)所在記憶體中的起始位址。 • 2.維度(dimension):代表此陣列為幾維陣列,如一維陣列、二維陣列、三維陣列等等。 • 3.索引上下限:指元素在此陣列中,記憶體所儲存位置的上標與下標。 • 4.陣列元素個數:是索引上限與索引下限的差+1。 • 5.陣列型態:宣告此陣列的型態,它決定陣列元素在記憶體所佔有的大小。
2-2 陣列 • 多維陣列也必須在一維的實體記憶體中表示,因為記憶體位置是依線性順序遞增。通常依照不同的語言,又可區分為 • 1、以列為主(Row-major):一列一列來依序儲存,例如Java、C/C++、PASCAL語言的陣列存放方式。 • 2、以行為主(Column-major):一行一行來依序儲存,例如Fortran語言的陣列存放方式。
2-2 陣列 • 一維陣列: • 例如假設A是一維陣列(One-dimension Array)名稱,如果宣告為A(1:u1),表示A含有n個元素,其中1為下標,u1為上標。 • 不過如果一維陣列A是宣告為A(l1:u1),且l1為小於或等於u1的任何整數,α為起始位址,d為單位空間,那麼公式如下: 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)所在的住址) Loc(A(i))=α+(i- l1)*d
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
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
2-2 陣列 • 在C語言,一維陣列的語法宣告為: • 資料型態: • 表示該陣列存放的資料型態,可以是基本的資料型態(如int,float,char…等),或延伸的資料型態,如結構型態(struct)等。 • 陣列名稱: • 命名規則與變數相同。 • 元素個數: • 表示陣列可存放的資料個數,為一個正整數常數,且陣列的索引值是從0開始。若是只有中括號,即沒有指定常數值,則表示是定義不定長度的陣列(陣列的長度會由設定初始值的個數決定)。 資料型態 陣列名稱[陣列長度];
2-2 陣列 • 在C語言中定義如下陣列,可如下圖表示: int Score[5];
#include <stdio.h> #include <stdlib.h> int main() { int Score[5]={ 87,66,90,65,70 }; /* 定義整數陣列 Score[5],並設定5筆成績 */ int count, Total_Score=0; 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); /* 輸出成績總分 */ system("pause"); return 0; } 2-2 陣列 • 範例 2.2.3 • 請使用C的一維陣列來計錄5個學生的分數,並使用for迴圈列印出每筆學生成績及計算分數總合
2-2 陣列 • 二維陣列(Two-dimension Array) : • 可視為一維陣列的延伸,都是處理相同資料型態資料,差別只在於維度的宣告。 • 例如一個含有m*n個元素的二維陣列A (1:m,1:n),m代表列數,n代表行數,各個元素在直觀平面上的排列方式如下矩陣,A[4][4]陣列中各個元素在直觀平面上的排列方式如下
2-2 陣列 • 依照不同的語言,可區分為兩種方式: • 以列為主(Row-major) • 以行為主(Column-major)
2-2 陣列 • 以列為主(Row-major): • 存放順序為a11,a12,...a1n,a21,a22,...,..amn,假設α為陣列A在記憶體中起始位址,d為單位空間,那麼陣列元素aij與記憶體位址有下列關係: Loc(aij)= α+n* (i-1)*d+(j-1)*d
2-2 陣列 • 以行為主(Column-major): • 存放順序為a11,a21,...am1,a12,a22,...,..amn,假設α為陣列A在記憶體中起始位址,d為單位空間,那麼陣列元素aij與記憶體位址有下列關係: Loc(aij)= α+(i-1)*d+m*(j-1)*d
2-2 陣列 • 宣告陣列A(1:2,1:4),表示法如下:
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
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個位置
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
2-2 陣列 • 範例 2.2.7 • A(-3:5,-4:2)的起始位址A(-3,-4)=1200,以row-major排列,每個元素佔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
#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"); } 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"); } system("pause"); return 0; } 2-2 陣列 • 範例 2.2.8 • 以下是數位新知公司三個業務代表2008年前六個月每人的業績,請使用二維陣列設計一C程式,計算以下結果: • (1)每個業務代表的前半年業績總額。 • (2)1~6月每個月這三個業務代表的總業績。
解答: #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。
2-2 陣列 • 多維陣列(1/3) • 在程式語言中,只要記憶體大小許可時,都可以宣告成更多維陣列來存取資料,通常凡是二維以上的陣列都可以稱作多維陣列。 • 三維陣列的表示法和二維陣列一樣,皆可視為是一維陣列的延伸,如果陣列A宣告為A(1:u1,1:u2,1:u3),表示A為一個含有u1*u2*u3元素的三維陣列,可以看作是一個立方體,如下圖
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
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
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
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
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
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+(3-0)=318+120+25+3=1266
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
2-2 陣列 • 在C中,凡是二維以上的陣列都可以稱作多維陣列 • 想要提高陣列的維度,只要在宣告陣列時,增加中括號與索引值即可。 • 定義方式如下所示: • 範例: 資料型態 陣列名稱[元素個數] [元素個數] [元素個數]……. [元素個數]; float No[2][2][2];
#include <stdio.h> #include <stdlib.h> int main() { int i,j,k,sum=0; 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++) { for(j=0;j<3;j++) { for(k=0;k<3;k++) { 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; } 2-2 陣列 • 範例 2.2.15 • 假設一個三維陣列元素內容如下: • 請設計一C程式來計算此陣列中的每個元素值總和,並將值為負數的元素值都換為0,再輸出新陣列的所有內容。 A[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)}};
#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 i,j,k,min=num[0][0][0];//設定main為num陣列的第一個元素 for(i=0;i<2;i++) for(j=0;j<3;j++) for(k=0;k<3;k++) if(min>=num[i][j][k]) min=num[i][j][k]; //利用三層迴圈找出最小值 printf("最小值= %d\n",min); system("pause"); return 0; } 2-2 陣列 • 範例 2.2.16 • 假設一個三維陣列元素內容如下: • 請設計一支C程式,利用三層巢狀迴圈來找出此2x3x3三維陣列中所儲存數值中的最小值。 int num[2][3][3]={{{33,45,67}, {23,71,56}, {55,38,66}}, {{21,9,15 }, {38,69,18}, {90,101,89}}}
2-2 陣列 • 結構陣列(1/6) • 結構與陣列的作用同樣是用來建立資料集合 • 以C現有的資料型態作為基礎,允許使用者建立自訂資料型態,又稱為衍生資料型態(derived data type)。 • 簡單來說,就是將一組有相關性卻擁有不同資料型態的資料組成一組新的資料型態。 • 結構的架構必須具有結構名稱與結構項目,而且必須使用關鍵字struct來建立。
2-2 陣列 • 結構陣列(2/6) • 結構的基本宣告方式如左下所示: • 在結構定義中可以使用基本的變數、陣列、指標,甚至是其它結構成員等。 • 注意在定義之後的分號不可省略 • 右上圖為一個結構定義例子,定義學生的姓名與成績 範例 struct 結構名稱 { 資料型態 結構成員1; 資料型態 結構成員2; ...... }; struct student { char name[10]; int score; int ID; };
2-2 陣列 • 結構陣列(3/6) • 定義了結構之後,便可直接使用它來建立結構物件 • 結構定義本身就像是個建構物件的藍圖或模子 • 結構物件則是根據這個藍圖製造出來的成品或模型 • 每個所建立的結構物件都擁有相同的結構成員 • 一個宣告建立結構物件的例子如下所示: • 也可以在定義結構的同時宣告建立結構變數,如下: struct student s1, s2; struct student { char name[10]; int score; int ID; } s1, s2;
2-2 陣列 • 結構陣列(4/6) • 建立結構物件之後,我們可以使用英文句號.來存取結構成員。 • 這個句號通常稱之為「點運算子」(dot operator) • 只要在結構變數後加上成員運算子"."與結構成員名稱,就可以直接存取該筆資料: • 例如我們可以如下設定結構成員 結構變數.項目成員名稱; strcpy(s1.name, "Justin"); s1.score = 90; s1.ID=10001;
2-2 陣列 • 結構陣列(5/6) • 結構陣列則是將衍生型資料型態(Structure data type)與陣列的共同應用。 • 結構則可以集合不同的資料型態,如果同時要記錄多筆相同結構資料,還是得宣告一個結構陣列型態。宣告方式如下: struct 結構型態名稱 結構陣列名稱[元素個素];
2-2 陣列 • 結構陣列(6/6) • 下面程式碼片段將建立具有五個元素的結構陣列: • 至於要存取的成員,在陣列後方加上“[索引值]”存取該元素即可,例如: struct student { char name[10]; int math; int english; }; struct student class1[5]; 結構陣列名稱[索引值].陣列成員名稱
2-2 陣列 • 字元陣列(1/3) • 在C語言中,並沒有稱為字串(string)的基本資料型態。 • 如果要在C程式中儲存字串,就必需使用字元陣列來表示。 • ‘a’是一個字元常數,是以單引號( ‘ )包括起來。 • “a”則是一個字串常數,是以雙引號( “ )包括起來。 • 兩者的差別在於字串的結束處會多安排1個位元組的空間來存放’\0‘字元(Null字元,ASCII碼為0),作為字串結束時的符號。
2-2 陣列 • 字元陣列(2/3) • C字串宣告方式有兩種: 方式1:char 字串變數[字串長度]="初始字串"; 方式2:char 字串變數[字串長度]={'字元1', '字元2', ...... ,'字元n', '\0'};
2-2 陣列 • 字元陣列(3/3) • 以四種宣告方式說明: • Str_4並不是字串常數,因為最後字元並不是'\0'字元,輸出時會出現奇怪的符號。 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', '!' };
#include<stdio.h> #include<stdlib.h> int main() { int length;/*用作計算字串的長度*/ char str[30]; printf("請輸入字串:"); /*輸入字串*/ gets(str); printf("輸入的字串為:%s\n",str); length=0; while (str[length]!='\0') length++; printf("此字串有%d個字元\n",length); system("pause"); return 0; } 2-2 陣列 • 範例 2.2.17 • 在字串的處理中,是字元陣列的應用。通常會需要知道字串的長度,請設計一C程式,並利用while迴圈逐一讀取字元,並計算此輸入字串的長度。如輸入"changeable",會輸出長度10。
#include <stdio.h> #include <stdlib.h> int main() { int i=0; char str[50]; printf("請輸入一個字串:"); gets(str); 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; } 2-2 陣列 • 範例 2.2.18 • 請設計一C程式,將所輸入字串中的大寫字母轉為小寫,小寫字母轉為大寫。如輸入"aPPle",會輸出"AppLE"。
#include <stdio.h> #include <stdlib.h> int main() { char arr2[50]; int i,sum; printf("請輸入一個字串:"); gets(arr2); 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; } 2-2 陣列 • 範例 2.2.19 • 請設計一C程式,將使用者輸入的原始字串資料反向排列輸出的程式。如"changeable",會輸出"elbaegnahc"。