260 likes | 414 Views
第一章. Introduction. 導論. 1 -1 資料與資訊. 資料 ( data ) 的定義:〝用具體符號表示,而能夠被計算機處理的資訊 ( information)〞 。 資料強調符號本身,而資訊則強調資料所呈現出來,經過人們分析、理解並領會的事實。 我們將在資料結構這門課中探討計算機系統所儲存以及處理的資料,並且學習如何組織這些資料,及處理這些資料的方法。. c 語言的基本資料型態有: int: 整數( Integer ) 16-bit, int i=2; long int: 長型整數( Integer ) 32-bit
E N D
第一章 Introduction 導論
1-1 資料與資訊 • 資料 ( data ) 的定義:〝用具體符號表示,而能夠被計算機處理的資訊 (information)〞。 • 資料強調符號本身,而資訊則強調資料所呈現出來,經過人們分析、理解並領會的事實。 • 我們將在資料結構這門課中探討計算機系統所儲存以及處理的資料,並且學習如何組織這些資料,及處理這些資料的方法。
c 語言的基本資料型態有: • int: 整數(Integer)16-bit, int i=2; • long int: 長型整數(Integer) 32-bit • float:單精確度浮點數 (Floating Point) 32-bit • double:雙精確度浮點數(Floating Point) 64-bit • char: 字元(Character ) 8-bit, char c='c'; • bool: 布林(Boolean) 1-bit • c 語言的組合資料型態有: • 陣列型態: char c[5] = { 'a', 'b', 'c', 'd', 'e' } • 結構型態: struct score { int math; int computer; } horng, chang;
1-2演算法與資料結構 • 資料結構這門課就是研究資料在記憶空間中的儲存方式,以便我們在設計程式處理問題時,能使用最佳的資料結構,並提供一種策略或方法能有效率的善用這些資料,以達到下列目的: • 執行程式速度快。 • 資料佔用最少的記憶空間。 • 更快速的存取這歇資料。 • 能有效率將這些資料轉換成有用的資訊的策略或方法就是 演算法 (algorithm) 。 • 演算法 + 資料結構 = 程式 方法策略 資料 資訊
1-2 資料結構與演算法 • 演算法 (algorithm) 的定義:“在有限步驟內解決數學問題的程序”。在計算機科學的領域中,演算法泛指“適合被實作為計算機程式的解題方法” • 演算法通常具有以下五個特性: 1‧輸入 (Input) 2‧輸出 (Output) 3‧明確性 (Definiteness) 4‧有限性 (Finiteness) 5‧正確性 (Correctness)或稱 有效性 (Effectiveness) • 演算法表達方法: 1‧一般的語言 2‧虛擬碼 3‧流程圖
例 歐幾里得演算法 計算兩個自然數的最大公因數 (Greatest Common Divisor, GCD) 演算法描述如下: 步驟 1. 輸入兩個自然數 A , B 步驟 2.A 除以B,餘數為 R 步驟 3.如果 R 為零,則跳至步驟 5 步驟 4. A←B,B←R,跳至步驟 2 步驟 5. B 即為 GCD
這些敘述符合演算法的特性: 一‧輸入 (Input) :兩個自然數 A, B 。 二‧明確性 (Definiteness):以逐步追蹤法 一步一步執行敘述,不造成混淆。 三‧正確性 (Correctness):以兩組 A, B 代入執行均得到正確結果。 四‧有限性 (Finiteness): A, B, R 在第二圈以後都會愈來愈小(因為每一圈 R 一定小於 B)。由於他們都是正整數,所以 R 終會等於零。 五‧輸出 (Output) :A, B 的最大公因數。
用流程圖來描述這個演算法: 開 始 輸入自然數A,B R = A MOD B 否 A ← B B ← R R = 0 ? 是 B為GCD 結 束
1-3 程式的分析 一個好的程式,通常必須滿足下列的條件: 一‧正確達到目的:通常以具有代表性的多組輸入來測試驗證 二‧可維護性高:程式的可維護性牽涉到編寫程式的方法和風格,以下是幾項檢驗項目: 檢驗項目一:編寫程式是否符合模組化的原則,提供由上而下 (Top-Down)的理解思路? 檢驗項目二:所有的常數變數及函式 (function) 名稱是否有意義? 檢驗項目三:所有函式的輸入、輸出及功能是否被明確地定義? 檢驗項目四:是否在適當的地方加上註解? 檢驗項目五:說明文件是否詳實完備? 三‧效率高:計算程式的時間複雜度來分析效率並尋求改良之道 四‧記憶體需求低:在不影響效率的情況下,需求愈低愈好
1-4 執行效率 一、C指令的執行步數 • 註釋: 0步. • 宣告指令: 0步. 例: int a[100]; • 運算式Expressions和指定指令 • <variable> = <expr> • 通常1步, 除了包括函式呼叫, 例: i++; a = b; 1步. d = get_key(a[i],pass,m); ?步, 由函式決定。 • 變數陣列<variable>由陣列大小決定。 例: int a[100], b[100]; 指定指令 a = b, 為100步.
C 指令的執行步數 • Switch指令 • 每一條件步數condition就是自己的成本加上所有的前面條件步數再加上<expr>的步數。 Switch (<expr>) { case cond1: <statement1> case cond2: <statement2> . . . default: <statement> } • if-else指令 if (<expr>) <statement1>; else <statement2>; • 計算if-else指令的執行步數由<expr>, <statement1>, 和 <statement2>決定。
C 指令的執行步數 • 函式步數: 通常1步, 除非包括有pass-by-value之參數傳遞及遞迴呼叫recursive invocation 之函式。 • 函式指令步數: void main( ) : 0步。 • 記憶體管理指令步數: malloc, free, sizeof : 1步。 • 跳躍指令步數: continue, break, goto, return : 1步。 return <expr>的執行步數由<expr>決定。 • I/O指令步數: printf, scanf: 1步。
二、計算執行步數的方法 • 在程式中加入count變數 • 例: 2n + 3 steps. // O(n) float sum (float *a, const int n) {float s = 0 ; count++ ; // count is global for (int i = 0; i< n; i++) { count++ ; // for for s += a[i] ; count++ ; // for assignment } count++ ; // for last time of for count++ ; // for return ; return s ; }
三、 while 迴圈的計數 計算 n 階層 ( n! ) 的函式如下: int factor( int n) { int result, i ; 1result = 1 ; 2 i = 1 ; 3 while ( i < = n ) 4 { 5 result = result * i ; 6 i = i + 1 ; 7 } 8 return result; }
行號 n>0 時 n <= 0 時 1 1 1 2 1 1 3 n+1 1 5 n 0 6 n 0 8 1 1 總次數 3n+4 4 我們計算每一行敘述被執行的次數: 如果 n>0,頻率計數為 3n+4次,如果 n <= 0 ,頻率計數為 4次
四、計算第n項之費氏級數f(n)的函式如下: • f(n) = f(n-1) + f(n-2), n >=2; f(0) = 0, f(1) = 1; • #include <stdio.h> • void main( ) • { int n, i, fn1, fn2, fn; • printf( "請輸入費氏級數的項數n ==> " ); • scanf( "%d", &n); • if (n <= 1) printf ( "[%d] \n", n); • else • { fn1=1; fn2=0; • printf ( "[0][1]"); • for ( i=2; i<=n; i++ ) • { fn = fn2 + fn1; • printf ( "[%d]", fn); • fn2 = fn1; • fn1 = fn; } • printf ( "\n"); } • }
#include <stdio.h> • void main( ) • { int n, i, fn1, fn2, fn; • printf("請輸入費氏級數的項數n==>"); • scanf( "%d", &n); • if (n <= 1) printf ( "[%d] \n", n); • else • { fn1 = 1; fn2 = 0; • printf ( "[0][1]"); • for ( i=2; i<=n; i++ ) • { fn = fn2 + fn1; • printf ( "[%d]", fn); • fn2 = fn1; • fn1 = fn; } • printf ( "\n"); } • } 如果n>1,頻率計數為 5n+7次,如果 n<=1,頻率計數為 4 次
五、 for迴圈的計數 A. 單層迴圈 1for ( i = 1 ; i < n ; i++) 2 result = result + 1 ; 我們計算每一行敘述被執行的次數: 行號 1: 條件成立 (if i < n ),頻率計數為 n-1次,條件不成立(if i = n ), 頻率計數為 1次; 總執行的次數為 n次。 行號 2: 迴圈計數器i的範圍是1…n-1,因此迴圈共重複 n-1 次, 總執行的次數為 n-1次。 簡單的數學模式: n-1 總次數 = Σ 1 = n-1 i=1
B. 雙層迴圈、內圈固定次數 1 for ( i = 0 ; i < n ; i++) 2 for ( j = 1 ; j < n ; j++) 3 result = result + 1 ; 我們計算每一行敘述被執行的次數: 行號 1: 條件成立 (if i < n ),頻率計數為 n 次,條件不成立(if i = n ), 頻率計數為 1 次; 總執行的次數為 n+1 次。 行號 2: 迴圈計數器 j 的範圍是1…n-1,因此,迴圈條件成立共執行 n*(n-1) 次,迴圈條件不成立共 n 次,總執行的次數為 n*(n-1) + n 次。 行號 3: 外圈迴圈計數器 i 的範圍是 0…n-1,內圈迴圈計數器 j 的範圍是 1…n-1,外圈每執行1圈,內圈就會執行 n-1 圈。而外圈會執行 n 圈,因此,第 3 行敘述共將執行 n * (n-1) 次。
C. 雙層迴圈、內圈不固定次數 1for ( i = 1 ; i < = n ; i++) 2 for ( j = i+1 ; i <= n ; j++) 3 result = result + 1 ; 我們計算每一行敘述被執行的次數: 行號 1: 條件成立 (如果 i <= n ),頻率計數為 n 次, 條件不成立(如果 i = n+1 ),頻率計數為 1 次; 總執行的次數為 n+1 次。 行號 2: 迴圈計數器 j 的範圍是 i + 1,…, n,( i =1, 2, ..., n; j = 2, 3, 4, ..., n), 因此,迴圈條件成立共執行 (n-1)+(n-2) + ... + 3 + 2 +1 + 0 = n * ( n-1 ) / 2 次, 迴圈條件不成立共 n 次,總執行的次數為 n*(n-1) + n 次。 行號 3: 第 3 行敘述共將執行 n * (n-1) / 2 次。
i 的值 j 的範圍 第3行執行次數 1 2 …… n n-1 2 3 …… n n-2 3 4 …… n n-3 … … n-1 n …… n 1 n n+1 …… n 0 總次數 = n * ( n-1 ) / 2 數學模式: n n-1 n n 總次數=ΣΣ1 = Σ( n-1-(i+1)+1) = Σ(n –i)= n2-n*(n+1)/2 = n*(n-1)/2 i=1j=i+1 i=1 i=1
六、Big-O符號 Big-O符號的數學定義為:若且唯若 f(n) = O(g(n)) 則存在大於 0 的常數 c 和 n0,使得對所有的 n 值,當n ≧ n0時,f(n) ≦ c * g(n) 均成立。 用數學式表示為: f(n) = O(g(n)) c, n0>0 n≧ n0 , f (n) ≦ c * g(n) 用口語解釋為:f(n) 取Big-O符號為O( g(n)),當 n 夠大的時候,g(n)相當 於是f(n)的上限 c*g(n) 用圖示可表達為: f(n) n n0
◎ 5n2 + 6n + 9 = O( n2 ) → f( n ) = 5n2 + 6n + 9, g( n ) = n2 → f 函數取Big-O符號為 g函數 →5n2 + 6n + 9 取 Big-O 符號為 O( n2 ) ◎ 3n lgn + 9n + 10 = O( nlgn ) ,因為 n lgn 的次數比n 大,取最高次項而不計係數時,自然取到 n lgn (lg n = log2n) ◎ 9 n2 + 6nlgn + 9 = O( n2 ) ,因為n2的次數最大,取最高次項而不計係數時,自然取到n2 ◎19n3 + 9 n2 + 6n+ 9 = O( n3 ) ,因為n3的次數最大,取最高次項而不計係數時,自然取到 n3
常見的時間複雜度等級有: • O(1):常數級 • O(lgn) :對數級 • O(n) :線性級 • O(nlogn) • O(n2) :平方級 • O(n3) :立方級 • O(2n) :指數級 • 它們的成長速度順序是: • O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) • 如果解決同樣問題的三個程式,他們的時間複雜度分別是O( n2 )、O( nlogn )、O( n3 ),時間複雜度為O( nlogn )的程式是比較有效率的。這就是概略分級的意義,使我們能簡單比較程式的效率。
常見演算法複雜度:O(1)、O(log n)、O(n)、 O(nlog n)、O(n2)、O(n3)、O(2 n) 、O(n!) 、O(n n) • 1年有31.536百萬秒。
2106 2n n3 n2 6000 nlogn 1000 n 10 logn 1 2 4 8 16 32 64 128