33
1 迴迴 & 迴迴 (Loop & Recursion) Kevingyc 2009/02/15

迴圈 & 遞迴 (Loop & Recursion)

Embed Size (px)

DESCRIPTION

迴圈 & 遞迴 (Loop & Recursion). Kevingyc 2009/02/15. 迴圈. 在 C 語言中,有以下幾種迴圈 (Loop) :. For Loop. While Loop. Do while Loop. For Loop. Format : for ( init-expression ; cond-expression ; loop-expression ) { statement }. Example : 請印出 1 到 10. int i; for(i = 1; i < 11; i++) { - PowerPoint PPT Presentation

Citation preview

Page 1: 迴圈  &  遞迴 (Loop & Recursion)

1

迴圈 & 遞迴(Loop & Recursion)

Kevingyc

2009/02/15

Page 2: 迴圈  &  遞迴 (Loop & Recursion)

2

迴圈

在 C 語言中,有以下幾種迴圈 (Loop) : For Loop

While Loop

Do while Loop

Page 3: 迴圈  &  遞迴 (Loop & Recursion)

3

For Loop

Format : for (for (init-expressioninit-expression ; ; cond-expressioncond-expression ; ; loop-expressionloop-expression ) {) {statementstatement } }

init-expression( 起始式 ) :a. 不一定需要b. 可多重宣告i = 0, k = 1, j = 3….;

cond-expression( 條件式 ) :a. 不一定需要 ( 但需要其他終止條件 )b. 條件可以不只 一個 ( i < 0 || k != 5 )

loop-expression( 循環式 ) :a. 迴圈每跑一次,便會執行一次b. 可以不只一個 i++, j = j +2,…c. 不一定需要Example : 請印出 1 到 10

int i;for(i = 1; i < 11; i++){ printf("%d ", i);}

Page 4: 迴圈  &  遞迴 (Loop & Recursion)

4

For Loop 的應用

int a[3][3]={0,1,2,3,4,5,6,7,8};int i,j; for(i=0;i<3;i++) { for(j=0;j<3;j++) { printf("%d ", a[i][j]); } printf(“\n”); }

int array[7]={1,2,3,4,5,6,7};int i; for(i=0;i<7;i++) { array[i] *= array[i]; printf("%d ", array[i]); }

a. 利用兩個 for loop 印出矩陣 ( 巢狀迴圈 ) :b. 把陣列的所有值平方並存回陣列:

Page 5: 迴圈  &  遞迴 (Loop & Recursion)

5

While Loop

Format : while (while (expressionexpression) {) {statementstatement } }

expression( 判斷式 ) :a. 判斷式的資料型態為 bool( 布林值 , True / False)b. 條件可以不只 一個 ( i < 0 || k != 5 )Example : 請印出 1 到 10

int i=1;while( i < 11){ printf("%d ", i); i++;}

int i;for(i = 1; i < 11; i++){ printf("%d ", i);}

==

Page 6: 迴圈  &  遞迴 (Loop & Recursion)

6

While & For

兩者是可以互換的 !

// While versionint i=1;while( i < 11 ){ printf("%d ", i); i++;}

// For versionint i;for(i = 1; i < 11; i++){ printf("%d ", i);}

起始式

條件式

循環式

指令群

我們用上一頁的例子來看看 :

Page 7: 迴圈  &  遞迴 (Loop & Recursion)

7

While Loop 的應用

int i= 1, keyIn, number, max = 0; printf(" 請輸入你想輸入的數字有幾個 \n"); scanf("%d",&keyIn); while(i<=keyIn) { printf(" 請輸入第 %d 個數字 :",i); scanf("%d",&number); if( number > max) { max = number; } i++; } printf(" 最大的數字是 : %d\n",max);

a. 請讓使用者輸入數字,並找出最大的數字:

Page 8: 迴圈  &  遞迴 (Loop & Recursion)

8

While Loop 的應用

while(scanf("%s",&number) != EOF){………………………….………………………….………………………….}

這個是在寫 ACM 題目的時候常用的技巧,讓使用者可以無限次的輸入資料,直到指定的終止條件時才停止迴圈

b. 請讓使用者輸入資料,直到輸入 EOF( ctrl + z ) 為止:

* EOF:

Windows : ctrl + Z

Linux : ctrl + D

Page 9: 迴圈  &  遞迴 (Loop & Recursion)

9

Do While Loop

Format : do { do { statement statement } while (} while (expressionexpression));;

expression( 判斷式 ) :a. 判斷式的資料型態為 bool( 布林值 , True / False)b. 條件可以不只 一個 ( i < 0 || k != 5 )

Example : 請印出 1 到 10

int i=1;do{ printf("%d ", i); i++;} while( i < 11);

do while 迴圈是 while 迴圈的變形,

某些特殊需求時會需要用到

NOTE. do while 迴圈至少會執行一次

Page 10: 迴圈  &  遞迴 (Loop & Recursion)

10

while & do while

如果我們把……expression = 肚子還餓 statement= 吃東西

while (expression) {statement } => 如果肚子還餓,就吃東西,直到肚子不餓

do { statement } while (expression)=> 先吃東西,如果肚子還餓就再吃東西,直到肚子不餓

我們看看實際的例子吧 !

Page 11: 迴圈  &  遞迴 (Loop & Recursion)

11

while & do while 的比較 -實際例子

// whileint i=11;while( i < 11){ printf("%d ", i); i++;}

// do whileint i=11;do{ printf("%d ", i); i++;} while( i < 11);

Page 12: 迴圈  &  遞迴 (Loop & Recursion)

12

迴圈的輔助指令 -Break & Continue

// break 的例子// 當 i == 3 時, break int i;for(i = 1; i < 11; i++){ if( i==3 ) { break; } printf("%d ", i);}

// continue 的例子// 當 i == 7 時, continueint i;for(i = 1; i < 11; i++){ if( i==7 ) { continue; } printf("%d ", i);}

Break : 執行到 break 時,就終止該迴圈 ( 只會跳出一層 !! ) Continue : 執行到 continue 時,就會跳到迴圈最下面 而直接繼續進行迴圈

Page 13: 迴圈  &  遞迴 (Loop & Recursion)

13

Break 小問題

int i,j;for(i = 1; i <= 5; i++){

for(j = 1; j <= 5; j++){

printf("%d ", j);if( i==j ) break;

}printf("\n");

}

1. 當程式運作時,結果為何?int i,j;for(i = 1; i <= 10; i++){

for(j = 1; j <= 5; j++){

break;printf("%d ", j);

}printf("%d ", i);

}

2. 當程式運作時,結果為何?

Break 、 Continue 、 goto 這些指令請盡量少用以免破壞程式結構 => 因為會影響程式的可讀性

Page 14: 迴圈  &  遞迴 (Loop & Recursion)

14

你不能不知道 -迴圈的四大爆點

1. 陣列操作不可以寫超過陣列大小

int a[3]={0,1,2};Int i;for(i=0 ;i<=4;i++){ printf("%d ",a[i]);}

如果我們寫的範圍比我們宣告的陣列還要大,

除了會造成溢位之外還有可能會發生 RE ( Runtime Error ) 的錯誤,

而且會跑出我們意想不到的東西,如圖中標示的那兩個數字

Page 15: 迴圈  &  遞迴 (Loop & Recursion)

15

你不能不知道 -迴圈的四大爆點

int i;for(i=0 ;i<=4;i++){ int j=i; printf("%d ",j);}printf("%d ",j); `j' undeclared (first use this function)

變數都有屬於他的有效範圍,

通常是在 { } 之間 ( 全域變數為整個程式 ) ,

如果使用變數超出他的有效範圍時,

就會發生 CE (Compile Error) 的錯誤,就像下面紅色的錯誤訊息一樣

2. 使用的變數都要在有效範圍內使用

Page 16: 迴圈  &  遞迴 (Loop & Recursion)

16

Additional : 變數生命週期 (Scope)

• 對某一個變數來說,可以使用到這個變數的程式範圍就稱為這個變數的作用範圍,或稱為變數的生命週期 (scope) 。• 由於區塊是階層式的,大區塊可以內含小區塊,大區塊內的變數也可以在內含區塊內使用。

範圍:範圍:

生於定義變數生於定義變數 !!

死於區塊結束死於區塊結束 !!

變數類型 變數週期

區域變數 (local variable)

自己所在的區塊中

全域變數(global variable)

整個程式

Page 17: 迴圈  &  遞迴 (Loop & Recursion)

17

Additional : 變數週期範例

int g = 7; // global variable// global variableint main(){ int i = 1; // local variable// local variable { char i = 'k'; // local variable// local variable int y = 2; // local variable// local variable printf("i = %d y = %d g = %d\n",i,y,g);

// printf 1 } printf("i = %d y = %d g = %d\n",i,y,g);

// printf 2 return 0; }

// printf 1

g =

i =

y =

// printf 2

g =

i =

y =

107

7

7

2

1

undeclared!!!

Page 18: 迴圈  &  遞迴 (Loop & Recursion)

18

你不能不知道 -迴圈的四大爆點

for(i= 0; ;i++ ){ } // 沒有設終止條件

for(i= 0; i<=5; ){ } // i 不動,所以不會結束

一定要告訴迴圈什麼時候該結束,否則迴圈會沒辦法結束,

而造成 TLE( Time Limit Exceeded ) 的錯誤,

像以上兩個寫法都會造成無限迴圈

3. 要設定合理的終止條件,

否則會造成無預期的無限迴圈 ( infinite loop )

Page 19: 迴圈  &  遞迴 (Loop & Recursion)

19

你不能不知道 -迴圈的四大爆點

while(1){while(2){while(3).............. ..............} .............}

寫很多層迴圈的時候,一定會有很多括號,

這時候對應的問題就很重要了

盡量不要省略任何大括號,並且把他整理清楚

這樣對以後要除蟲 (Debug) 的時候會很有幫助

4. 注意括號的對應,

尤其是當巢狀迴圈寫很大很複雜的時候 while(1) { while(2) { while(3) { .............. } ............... } ............. }

Page 20: 迴圈  &  遞迴 (Loop & Recursion)

20

迴圈小結

迴圈是一個程式裡面最基本的元件,所以一定要活用他,但是要注意以上的四個爆點,千萬不要去觸碰他,否則你的程式就會轟一聲地爆炸了!

當你很明確的知道迴圈什麼時候會結束,用 for 迴圈可以有比較清楚的架構;但是當你不確定迴圈什麼時候會結束的時候,反而用 while 迴圈會比較合適些。這些狀況在往後的程式設計上會很常碰到,仔細想想你要的終止條件到底是什麼,以及你需要做些什麼事情,就可輕易寫出正確的迴圈啦 !!

Page 21: 迴圈  &  遞迴 (Loop & Recursion)

21

遞迴 什麼是遞迴 (Recursion)?

就像剝洋蔥一樣,一層一層的往裡面剝

所謂 recursion ,就是” function 在自己裡面呼叫自己”,這種結構就叫做 recursion 。

把問題拆成「較小的相同問題」,把小問題解決了,大問題自然跟著解了。

Example : 請印出 1 到 10

void print_all(int i){

if(i!=0) {

print_all(i-1); printf("%d ",i);

}}

Page 22: 迴圈  &  遞迴 (Loop & Recursion)

22

遞迴的基本結構 void recur(int k){ if(k!=0) { printf("%d ",k); recur(k-1); }}

假設我們有個程式是長這樣

當 k = 4 的時候…

void recur(int 4){ if(4!=0) { printf("%d ",4); recur(4-1); }}

void recur(int 3){ if(3!=0) { printf("%d ",3); recur(3-1); }}

void recur(int 2){ if(2!=0) { printf("%d ",2); recur(2-1); }}

void recur(int 1){…….}

程式是這樣子運作的…

Page 23: 迴圈  &  遞迴 (Loop & Recursion)

23

遞迴的基本結構 void recur(int k){ if(k!=0) { printf("%d ",k); recur(k-1); }}

好像有點不好懂,那換一個角度來想,如果現在有四個副程式

當 k = 4 的時候…

void recur(int 4){ if(4!=0) { printf("%d ",4); recur1(4-1); }}

void recur1(int 3){ if(3!=0) { printf("%d ",3); recur2(3-1); }}

void recur2(int 2){ if(2!=0) { printf("%d ",2); recur3(2-1); }}

void recur3(int 1){…….}

recur == recur1 == recur2 == recur3

程式是這樣子運作的…

Page 24: 迴圈  &  遞迴 (Loop & Recursion)

24

遞迴的基本結構

void recur(int k){ if(k!=0) { printf("%d ",k); recur(k-1); printf("%d ",k); }}

recur(4)

基本上程式遞迴運作的情況會像下面這個圖所表示的狀況

if k == 4

recur(3)

recur(2)

recur(1)

recur(0)

True, Print(4)

False, return

Print(1), return

Print(2), return

Print(3), return

Print(4)

K =

Output :

True, Print(3)

True, Print(2)

True, Print(1)

4 3 2 1 1 2 3 4

43210

Page 25: 迴圈  &  遞迴 (Loop & Recursion)

25

遞迴的應用 - 費式數列 ( 遞迴式 )

F(n) = F(n-1) + F(n-2), F(1) = F(2) = 1

以 n=5 來舉例 :

F(5) = F(4) + F(3)

F(4) = F(3) + F(2)

F(3) = F(2) + F(1)

F(2) = 1

F(1) = 1+

F(5) = F(4) + F(3) = [ F(3) + F(2) ] + [ F(2) + F(1) ]

F(5) = { [ F(2) + F(1) ] + F(2) } + [ F(2) + F(1) ]

F(5) = 1 + 1 + 1 + 1 + 1 = 5

=>

int F(int n){ if(n==1 || n==2) { return 1; } else { return f(n-1) + f(n-2); }}

Page 26: 迴圈  &  遞迴 (Loop & Recursion)

26

遞迴的應用 - 費式數列 ( 圖表 )F(n) = F(n-1)+F(n-2), F(1) = F(2) = 1

以 n=5 來舉例 :F(5)=

F(4)= F(3)=

F(3)= F(2)= F(2)= F(1)=

F(1)=F(2)= 1 1

1 1 1

2

2

3

5

int F(int n){ if(n==1 || n==2) { return 1; } else { return f(n-1) + f(n-2); }}

Page 27: 迴圈  &  遞迴 (Loop & Recursion)

27

遞迴的應用 - 求 GCD( 最大公因數 )

int gcd( int a, int b ) // 全部擠在一行{ return (a%b!=0) ? gcd(b,a%b): b;}

gcd(4005,2403)

return gcd(2403,1602)

return gcd(1602,801) = 801

= 801

= 801int gcd( int a, int b ){ if(a%b!=0) return gcd(b,a%b); else return b; }

Page 28: 迴圈  &  遞迴 (Loop & Recursion)

28

迴圈 & 遞迴 兩者在大部分的情況下是可以互換的 !

int gcd( int a, int b ) // recursion{ return (a%b!=0) ? gcd(b,a%b): b;}

int gcd(int a,int b) // loop{ int x; do {

x=a%b;a=b;b=x;

}while(x!=0); return x;}

當我們在 coding 的時候,有些時候用迴圈會比較好寫,有時候用遞迴比較簡單,但大部分時候兩者是可以互相轉換的

Page 29: 迴圈  &  遞迴 (Loop & Recursion)

29

遞迴的進階思考 - 連通問題 什麼叫做連通 ?

顧名思義,

連通就是連在一起並成為一個通路

0 1 0 0

0 0 A 1

1 1 1 0

假設 A是一個人 ,他能走到的點是他四周的八個方格裡 ,數字為 1 的點 .

能走的點 , 即為”連通”點。依各題型的不同 ,所問的問題也不一樣 ,但概

念都是類似的。像油田數量 , 走迷宮…都是屬於連通問題

Page 30: 迴圈  &  遞迴 (Loop & Recursion)

30

連通問題範例ACM 第 352 題就是個典型的連通問題

“ 假設有一塊地圖,我們以 0 和 1 來表示不同的區塊,問 1 在這個地圖中有幾塊 ( 與四周的八格彼此連通的都算同一塊 ) ?

0 0 0 0

0 0 0 0

0 0 0 0 0 0

0 0 0 0

0 0 0

0 0 0 0

11 11

111111

1111

11 11

11110 1 2 3 4 5

0

1

2

3

4

5

區塊數 : 123

1. 發現 1, 並將他標記成0

3. 檢查四周是否有 1, 若有 , 將其標記為 04. 遞回檢查四周是否有 1,若有 , 將其標記為 0

2. 區塊數加 1

000 00

00 00

00

000000

00

00

00

FINISH!!!

Page 31: 迴圈  &  遞迴 (Loop & Recursion)

31

遞迴小結

何時該使用遞迴,是依照問題的解決方法來看,如果需要做同一件事情很多次,而且你不知道到底要做幾次的話,用遞迴可能是比較好的選擇,當然用迴圈還是可以寫出來。

當你程式語言越學越多、越學越深的時候,你會發現很多的函式都有用到遞迴的觀念,所以不要害怕使用遞迴,他是非常直覺的一種思考方法。

Page 32: 迴圈  &  遞迴 (Loop & Recursion)

32

練習題

基本題 :( 迴圈 )488 Triangle Wave100 The 3n + 1problem( 遞迴 )10696 F91408 Uniform Generator10922 2 the 9s

進階題 :  10018 Reverse and Add 10035 Primary Arithmetic572 Oil Deposits

Page 33: 迴圈  &  遞迴 (Loop & Recursion)

33

參考資料

08年寒訓講義07年寒訓講義06年寒訓講義On Internet