1 / 33

迴圈 & 遞迴 (Loop & Recursion)

迴圈 & 遞迴 (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++) {

Download Presentation

迴圈 & 遞迴 (Loop & Recursion)

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 迴圈 & 遞迴(Loop & Recursion) Kevingyc 2009/02/15

  2. 迴圈 • 在C語言中,有以下幾種迴圈(Loop): • For Loop • While Loop • Do while Loop

  3. For Loop • Format : for (init-expression; cond-expression ; loop-expression) {statement} Example : 請印出1到10 int i; for(i = 1; i < 11; i++) { printf("%d ", i); }

  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”); } a. 利用兩個for loop印出矩陣( 巢狀迴圈 ) : b. 把陣列的所有值平方並存回陣列: 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]); }

  5. While Loop • Format : while (expression) {statement } Example : 請印出1到10 int i=1; while( i < 11) { printf("%d ", i); i++; } int i; for(i = 1; i < 11; i++) { printf("%d ", i); } ==

  6. While & For • 兩者是可以互換的! 我們用上一頁的例子來看看 : // While version int i=1; while( i < 11 ) { printf("%d ", i); i++; } // For version int i; for(i = 1; i < 11; i++) { printf("%d ", i); }

  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.請讓使用者輸入數字,並找出最大的數字:

  8. While Loop的應用 b.請讓使用者輸入資料,直到輸入EOF( ctrl + z )為止: while(scanf("%s",&number) != EOF) { …………………………. …………………………. …………………………. } *EOF: Windows : ctrl + Z Linux : ctrl + D 這個是在寫ACM題目的時候常用的技巧, 讓使用者可以無限次的輸入資料, 直到指定的終止條件時才停止迴圈

  9. Do While Loop • Format : do { statement } while (expression); Example : 請印出1到10 int i=1; do { printf("%d ", i); i++; } while( i < 11); do while 迴圈是while 迴圈的變形, 某些特殊需求時會需要用到 NOTE. do while 迴圈至少會執行一次

  10. while & do while • 如果我們把…… expression=肚子還餓 statement=吃東西 while (expression) {statement } => 如果肚子還餓,就吃東西,直到肚子不餓 do { statement } while (expression) =>先吃東西,如果肚子還餓就再吃東西,直到肚子不餓 我們看看實際的例子吧!

  11. while & do while的比較-實際例子 // do while int i=11; do { printf("%d ", i); i++; } while( i < 11); // while int i=11; while( i < 11) { printf("%d ", i); i++; }

  12. 迴圈的輔助指令-Break & Continue • Break : 執行到break時,就終止該迴圈 ( 只會跳出一層!! ) • Continue : 執行到continue時,就會跳到迴圈最下面 而直接繼續進行迴圈 // break的例子 // 當 i == 3時,break int i; for(i = 1; i < 11; i++) { if( i==3 ) { break; } printf("%d ", i); } // continue的例子 // 當 i == 7時,continue int i; for(i = 1; i < 11; i++) { if( i==7 ) { continue; } printf("%d ", i); }

  13. Break小問題 Break、Continue、goto這些指令請盡量少用 以免破壞程式結構=>因為會影響程式的可讀性 1.當程式運作時,結果為何? 2.當程式運作時,結果為何? int i,j; for(i = 1; i <= 5; i++) { for(j = 1; j <= 5; j++) { printf("%d ", j); if( i==j ) break; } printf("\n"); } int i,j; for(i = 1; i <= 10; i++) { for(j = 1; j <= 5; j++) { break; printf("%d ", j); } printf("%d ", i); }

  14. 你不能不知道-迴圈的四大爆點 1.陣列操作不可以寫超過陣列大小 int a[3]={0,1,2}; Int i; for(i=0 ;i<=4;i++) { printf("%d ",a[i]); } 如果我們寫的範圍比我們宣告的陣列還要大, 除了會造成溢位之外還有可能會發生RE ( Runtime Error )的錯誤, 而且會跑出我們意想不到的東西,如圖中標示的那兩個數字

  15. 你不能不知道-迴圈的四大爆點 2.使用的變數都要在有效範圍內使用 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)的錯誤,就像下面紅色的錯誤訊息一樣

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

  17. Additional : 變數週期範例 int g = 7; // global variable int main() { int i = 1; // local variable { char i = 'k'; // local variable int y = 2; // 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 = 7 107 2 // printf 2 g = i = y = 7 1 undeclared!!!

  18. 你不能不知道-迴圈的四大爆點 3.要設定合理的終止條件, 否則會造成無預期的無限迴圈( infinite loop ) for(i= 0; ;i++ ){ } // 沒有設終止條件 for(i= 0; i<=5; ){ } // i不動,所以不會結束 一定要告訴迴圈什麼時候該結束,否則迴圈會沒辦法結束, 而造成 TLE( Time Limit Exceeded )的錯誤, 像以上兩個寫法都會造成無限迴圈

  19. 你不能不知道-迴圈的四大爆點 4.注意括號的對應, 尤其是當巢狀迴圈寫很大很複雜的時候 while(1) { while(2) { while(3) .............. .............. } ............. } while(1) { while(2) { while(3) { .............. } ............... } ............. } 寫很多層迴圈的時候,一定會有很多括號, 這時候對應的問題就很重要了 盡量不要省略任何大括號,並且把他整理清楚 這樣對以後要除蟲(Debug)的時候會很有幫助

  20. 迴圈小結 • 迴圈是一個程式裡面最基本的元件,所以一定要活用他,但是要注意以上的四個爆點,千萬不要去觸碰他,否則你的程式就會轟一聲地爆炸了! • 當你很明確的知道迴圈什麼時候會結束,用for迴圈可以有比較清楚的架構;但是當你不確定迴圈什麼時候會結束的時候,反而用while迴圈會比較合適些。這些狀況在往後的程式設計上會很常碰到,仔細想想你要的終止條件到底是什麼,以及你需要做些什麼事情,就可輕易寫出正確的迴圈啦!!

  21. 遞迴 void print_all(int i) { if(i!=0) { print_all(i-1); printf("%d ",i); } } • 什麼是遞迴(Recursion)? 就像剝洋蔥一樣, 一層一層的往裡面剝 Example : 請印出1到10 所謂recursion,就是”function在自己裡面呼叫自己”, 這種結構就叫做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) { ……. } 程式是這樣子運作的…

  23. 遞迴的基本結構 void recur(int k) { if(k!=0) { printf("%d ",k); recur(k-1); } } 好像有點不好懂,那換一個角度來想,如果現在有四個副程式 當 k = 4的時候… recur == recur1 == recur2 == recur3 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) { ……. } 程式是這樣子運作的…

  24. 遞迴的基本結構 基本上程式遞迴運作的情況會像下面這個圖所表示的狀況 if k == 4 K = 2 4 1 0 3 Output : 4 3 2 1 1 2 3 4 True, Print(4) recur(4) Print(4) True, Print(3) recur(3) Print(3), return void recur(int k) { if(k!=0) { printf("%d ",k); recur(k-1); printf("%d ",k); } } recur(2) True, Print(2) Print(2), return recur(1) True, Print(1) Print(1), return recur(0) False, return

  25. 遞迴的應用-費式數列 ( 遞迴式 ) F(n) = F(n-1) + F(n-2), F(1) = F(2) = 1 int F(int n) { if(n==1 || n==2) { return 1; } else { return f(n-1) + f(n-2); } } 以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

  26. int F(int n) { if(n==1 || n==2) { return 1; } else { return f(n-1) + f(n-2); } } 遞迴的應用-費式數列 ( 圖表 ) F(n) = F(n-1)+F(n-2), F(1) = F(2) = 1 以n=5來舉例: F(5)= 5 3 F(4)= F(3)= 2 2 F(3)= F(2)= 1 F(2)= 1 F(1)= 1 F(2)= 1 F(1)= 1

  27. 遞迴的應用-求GCD(最大公因數) gcd(4005,2403) = 801 int gcd( int a, int b ) { if(a%b!=0) return gcd(b,a%b); else return b; } return gcd(2403,1602) = 801 return gcd(1602,801) = 801 int gcd( int a, int b ) // 全部擠在一行 { return (a%b!=0) ? gcd(b,a%b): b; }

  28. 兩者在大部分的情況下是可以互換的! 迴圈 & 遞迴 當我們在coding的時候,有些時候用迴圈會比較好寫,有時候用遞迴比較簡單,但大部分時候兩者是可以互相轉換的 Loop Recursion int gcd(int a,int b)// loop { int x; do { x=a%b; a=b; b=x; }while(x!=0); return x; } int gcd( int a, int b ) // recursion { return (a%b!=0) ? gcd(b,a%b): b; }

  29. 遞迴的進階思考-連通問題 • 什麼叫做連通? 顧名思義, 連通就是連在一起並成為一個通路 假設A是一個人,他能走到的點是他四周的八個方格裡,數字為1的點. 能走的點,即為”連通”點。依各題型的不同,所問的問題也不一樣,但概 念都是類似的。像油田數量,走迷宮…都是屬於連通問題

  30. 連通問題範例 ACM第352題就是個典型的連通問題 “假設有一塊地圖,我們以0和1來表示不同的區塊,問1在這個地圖中有幾塊(與四周的八格彼此連通的都算同一塊)? 0 1 2 3 4 5 區塊數 : 0 1 2 3 0 0 1 1 0 1 0 1 0 1.發現1,並將他標記成0 1 2.區塊數加1 2 3.檢查四周是否有1,若有,將其標記為0 1 1 0 0 3 4.遞回檢查四周是否有1,若有,將其標記為0 1 1 1 0 0 0 4 1 0 0 1 5 FINISH!!!

  31. 遞迴小結 • 何時該使用遞迴,是依照問題的解決方法來看,如果需要做同一件事情很多次,而且你不知道到底要做幾次的話,用遞迴可能是比較好的選擇,當然用迴圈還是可以寫出來。 • 當你程式語言越學越多、越學越深的時候,你會發現很多的函式都有用到遞迴的觀念,所以不要害怕使用遞迴,他是非常直覺的一種思考方法。 一定要設終止條件!! 否則就會無窮遞迴到 system stack overflow!!

  32. 練習題 基本題 : (迴圈) 488 Triangle Wave 100 The 3n + 1problem (遞迴) 10696 F91 408 Uniform Generator 10922 2 the 9s 進階題:  10018 Reverse and Add 10035 Primary Arithmetic 572 Oil Deposits

  33. 參考資料 • 08年寒訓講義 • 07年寒訓講義 • 06年寒訓講義 • On Internet

More Related