看似比較簡單的推坑教學
~~C語言崩潰到崩潰EX(二)~~
編者:lian0123
學歷:南大資工學生
   大安高工畢業
本文採用CC授權協定
更新日期:2017/08/23 維護期限:2018/09/01版本編號1.0.0+
前言
•本人的歷鍊還不夠,所以文章有地方錯,歡迎各位大神指出,感
謝
•我會嘗試以最簡單方式去進行教學,讓大部分的人能聽的懂。所
以拜託大神們在打臉我時,請不要打的太大力
•以下文章有參照:
• ISO 9899:2011(C11)公開版本的"WG14 N1570"文件
撰寫此文章的原因(一)
•過去我曾看過金門大學的陳鍾誠教授的十分鐘系列與成功大學的
jserv的你所不知道的C語言系列與眾多網路開源的教學文章,我
也想為程式界做出一些貢獻,所以撰寫了此類型的文章
•雖然看起來有點簡陋的程式教學,但這也算是我努力寫出來的,
但由於要寫多,所以可能會有錯誤或漏寫與打錯字的地方,請見
諒或通知我
撰寫此文章的原因(二)
•基於大部分非資工科系但對程式有興趣的學生、部份資工科系但
程式能力不強的學生、還有之後入學的新生們.....
•建議去讀一本計算機概論(中文或英文都可以),會比較好閱讀
•加上前一版,我其實是剛一邊看完C語言的書一邊完成的,所以
本次進行了添增了大量補充、修正、ISO9899條文
•我到底該不該去寫個gitbook呢?(猶豫中)
申明
•有關於pointer的中譯:
• 雖然說比較常被翻作"指針",筆者還是比較習慣翻成"指標"
• 所以以下有關於pointer的中譯名稱皆用"指標"一詞代替
目錄
• Class 11 指標
• Class 12 讀檔與寫檔
• Class 13 結構
• Class 14 前處理器的應用
• Class 15 random
• Class 16 記憶體控管
• Class 17 如何寫個一好的程式碼
• Class 18 料結構概念說明
• Class 19 演算法概念說明
• Class 20 總結
Class 11 指標
關於指標的說明(一)
•據國際 ISO9899:2011文件中,6.2.5 Types 第20點底下:
• A pointer type may be derived from a function type or an object type,
called the referenced type. A pointer type describes an object whose
value provides a reference to an entity of the referenced type. A
pointer type derived from the referenced type T is sometimes called
"pointer to T ". The construction of a pointer type from a referenced
type is called "pointer type derivation". A pointer type is a complete
object type.
• 部份的重點翻譯:
• 指標型別:可以從"function"或"物件"的型別產生
• 指標型別:描述一個對象,指標值提供了"引用型別"的實體引用
關於指標的說明(二)
詳細請參閱 國際 ISO9899:2011文件中,6.3.2.3 Pointers 條目
何謂指標?
•當我們執行「int a=10 *b;」時,以最簡單的方式來說
•int *b就是宣告b是一個指標
•我們可以將b指標指到a所在的記憶體位置上
•也就是讓b直接去獨a的資料
a
10
b
宣告指標
•int *b; //宣告指標(指向變數用)
•int **b; //宣告指標的指標(指向指標用)
指標的指標
•將一指標指到另一個指標上
•而另一個指標對應於記憶體
•在這情況 c 、b 、a 皆指向於
同一個記憶體上,所以呼叫每
一個的變數都是會回傳10
a
10
b
c
指派指標
•int a=10,*b,**c;
•b =&a; //將變數b指向a的記憶體位置
•c =&b;//將變數c指向b的記憶體位置(因為b以指定到a的記憶體
位置上,所以c也會得到a的記憶體位址)
實際上指標與電腦記憶體(一)
•當我們宣告了一個指標,如下圖所示:
指向b
int空間
位址
50
int *a;
因為&a的屬性是int
所以佔走int大小空間
1000 1000
a=&b
int b=50
1005 1005
指向
此時我們呼叫*a時
就會回傳50
實際上指標與電腦記憶體(二)
•即便b變數改變時,我們還是能透過*a去取得b記憶體上的變數
指向b
int空間
位址
60b=60
我們存取*a
指向b
int空間
位址
60
60
當我們一開始宣告指標 指標會指向何處?
•答:NULL
• 即空,NULL的概念與0是不同的
• 用東西來比喻:NULL是什麼都沒有,0卻是一個空盒子(佔有空間)
0的概念NULL的概念
指標的實做
實做結果
副程式多變數的傳址
•實做方式為宣告:void function(int *m,int *n);
•實做呼叫:function(&a,&b);
•副程式內容:void function(int *m,int *n){....}
• 此時因為要直接對a、b做修改,所以要副程式中直接改*m與*n
Class 12 讀寫檔案
讀寫檔案的目的
•資料讀取
•儲存資料
•紀錄
•......
所謂的讀寫檔
•最簡單的說法:
• 調用資料流,去讓程式去對檔案進行操作
•是不是很簡單啊?(聽了這一句話誰會覺得"簡單"啊!)
FILE型別
•據國際 ISO9899:2011文件中,7.21.1 Introduction 第2點前段:
• The types declared are size_t (described in 7.19);
FILE
• which is an object type capable of recording all the information
needed to control a stream, including its file position indicator, a
pointer to its associated buffer (if any), an error indicator that records
whether a read/write error has occurred, and an end-of-file indicator
that records whether the end of the file has been reached
• 即FILE類型:是能夠記錄控制流的所有信息的對像類型,包括其文件位
置指示符,指向其相關聯的緩衝器(如果有的話)的指標
開檔函數 "fopen(...)"
•範例:
"接收fopen(...)中return 的FILE資料變數 "=fopen("檔案名 ","模式 ");
講解 "fopen(...)"函數(一)
•據國際 ISO9899:2011文件中,7.21.5.3 The fopen function:
•Synopsis
• #include <stdio.h> 引入標頭檔
• FILE*fopen(constchar*restrictfilename,constchar*restrictmode);使用方式
•Description
• The fopen function opens the file whose name is the string pointed to
by filename,and associates a stream with it.
• The argument mode points to a string. If the string is one of the
following, the file is open in the indicated mode. Otherwise, the
behavior is undefined
講解 "fopen(...)"函數(二)
• 即fopen函數會依照檔案名稱欄( restrictfilename)的字串去開啟檔案
• 而檔案位置是以你的程式碼檔案位置去尋找要存取檔案的相對位置
• 如果檔案名稱欄之內容實際上不存在,則其行為未定義
講解 "fopen(...)"函數(三)
restrict mode引數傳遞之內容
邏輯範例 "fopen(...)"函數
•回傳&FILE fopen("檔案名稱","狀態");
•FILE *input ;
•input = fopen("這是個可愛的文件.txt","r");
•這時我們會開啟對"這是個可愛的文件.txt"的資料流,並設定為對
其進行讀取,且將相關位址設定給input指標
EOF的概念
•全名:End Of File
•據國際 ISO9899:2011文件中,7.21.1 Introduction 第三點:
•EOF
• which expands to an integer constant expression, with type int and a
negative value, that is returned by several functions to indicate end-
of-file, that is, no more input from a stream;
• 它擴展為整數常量表達式,其類型為int和負值,由幾個函數返回以指示
文件結尾,即不再有來自流的輸入
• 就Visual Studio中C程式EOF被定義為 -1
讀檔
讀檔用的文件(a.txt)
讀檔文件(a.txt)內容
讀檔的程式碼
更正:12行至13行間需加上一行fclose(input);
目的是安全關閉讀檔用的資料流
讀檔的程式實做
寫檔
寫檔的程式碼
更正:13行至14行間需加上一行fclose(output);
目的是安全關閉寫檔用的資料流
寫檔的程式實做
寫檔完成後的資料夾
寫檔完後的a.txt
常見的錯誤 讀檔時找不到檔案
•解決方法:建立檔案,確認是否有大小寫不同或是否忘了副檔名
•這情況只會在讀檔情況下發生,寫檔時會自動建立檔案後再寫入
常見的錯誤 指標錯誤存取
•解決方式:不要讓指標超過程式所使用的變數記憶體讀取的範圍
•當你在讀檔時不斷去讀取(不在EOF跳出而繼續讀取):
• 會讀到文件的資料,最後會跳出錯誤存取(不信的話,可以自行嘗試)
讀寫檔
•雖然說上述之範例僅讀檔、寫檔,以下說明何謂讀寫檔:
• 簡單來說就是在同一個程式內進行讀檔和寫檔:
FIlE *input;
FIlE *output;
input = fopen("OpenFile.txt","r");
output = fopen("OpenFile.txt","w");
//做讀寫檔
fclose(input);
fclose(output);
Class 13 結構
何謂結構?(一)
struct 結構名稱 {
定義資料型態 變數 ;
定義資料型態 變數 ;
定義資料型態 變數 ;
}宣告賦予結構的物件名稱(可多數);
賦予結構的物件名稱.結構名稱內的變數名;
何謂結構?(二)
• 也可寫成:
struct 結構名稱 {
定義資料型態 變數 ;
定義資料型態 變數 ;
定義資料型態 變數 ;
};
結構名稱 宣告用型態名稱;
宣告用型態名稱 結構變數名;
賦予結構的物件名稱.結構名稱內的變數名;
結構
#include<stdio.h>
int main(void){
struct my_test {
int a[10] ;
char b[8] ;
int c ;
int d ;
}M,N ;
M.c = 10;
N.c = 20;
結構(續)
printf("M.c is: %d n",M.c);
printf("N.c is: %d n",N.c);
return 0;
}
/*因為我們是將相同的結構賦予給M和N所以M.c和N.c是不一樣的東西*/
Output: M.c is: 10
N.c is: 20
當然 結構也能這樣宣告
struct 結構名稱 {
定義資料型態 變數 ;
定義資料型態 變數 ;
定義資料型態 變數 ;
};
結構名稱 宣告用型態名稱;
宣告用型態名稱 結構變數名;
賦予結構的物件名稱.結構名稱內的變數名;
結構另一種寫法
#include<stdio.h>
int main(void){
struct my_test {
int a[10] ;
char b[8] ;
int c ;
int d ;
} ;
my_test my_T;
my_T M,N;
M.c = 10;
N.c = 20;
結構另一種寫法(續)
printf("M.c is: %d n",M.c);
printf("N.c is: %d n",N.c);
return 0;
}
/*因為我們是將相同的結構賦予給M和N所以M.c和N.c是不一樣的東西*/
Output: M.c is: 10
N.c is: 20
結構解說(一)
•M.c與N.c的關係
將struct的內容放入M 將struct的內容放入N
提取M裡面的c
並在N裡面的C輸入20
宣告my_test的內容結構
(上述的a,b,c,d型態與命名)
提取N裡面的c
並在N裡面的C輸入20
結構解說(二)
•而結構變數名M底下包含的變數,正式的名稱是"成員 members"
•而在結構中:
• 定義一般的變數成員,需以.去進行指標呼叫
• 定義指標成員,需以->去進行指標呼叫
結構解說(三)
•據國際 ISO9899:2011文件中,6.5.2.3 Structure and union
members:
• The first operand of the . operator shall have an atomic, qualified, or
unqualified structure or union type, and the second operand shall
name a member of that type.
• 即:在.運算子前面的第一個需合格,.運算子後面的變數將命名該類型的
成員
※此處的.是指成員運算子
補充:
•在此沒提到union的結構:
• 在同一個記憶體空間上,用兩種(含以上)的資料組合去變換儲存
• 早期,記憶體空間不夠時的作法之一
int
floatstruct a
struct b
第一種
第二種
Class 14 前置處理器的處理
何謂前處理器?
引入標頭檔
檢查定義
常數定義
......
標頭檔中的引入函式庫
•語法:
#include <系統標頭檔檔名.h >
#include "手刻標頭檔檔名.h "
•EX:
#include<stdio.h>
#include <sys/type.h>
#include "MyHead.h"
標頭檔中的定義
•語法:
#define 名稱
#define 名稱 數值
•EX:
#define A
#define B 50
此時的B以被為常數,當我們呼叫B,會呼叫到50
標頭檔中的判斷
•語法:
#ifndef MYHEADER_H
#define MYHEADER_H
//程式內容
#endif
•EX:
#ifndef _GCD_H
#define _GCD_H
#include <linux/compiler.h>
unsigned long gcd(unsigned long a, unsigned long b) __attribute_const__;
#endif /* _GCD_H */
上述程式碼來自於:Linux Kernel 4.11 RC1/include/linux/gcd.h
Class 15 random
使用亂數
•當我們有一亂數時,我們要產生6種平均的可能:
•我們能將此亂數%6,用亂數取6的餘數;
• EX:亂數145%6 :餘1
亂數146%6 :餘2
亂數147%6 :餘3
亂數148%6 :餘4
亂數149%6 :餘5
亂數150%6 :餘0
以上即實施6種平均機率的仿做的概念
亂數應用(一)
•當我們製作猜數字遊戲時,我們總不能讓每次玩家猜到的數字都
一樣
•如下圖所示:
答案是0
你的數字永遠0
就像你的人生一樣
亂數應用(二)
•避免了上述的情況,我們可以調用系統的亂數表
•亂數表:
• 一個以雜湊亂數所製作成的列表
• 我們可以用此列表生成亂數
• 並應用一些運算去做限定範圍的亂數產生,EX:用亂數取餘數
亂數應用(三)
•但我們所調用的亂數表是屬於原本就存在的亂數表,所以數值是
固定的
•而要這些固定的亂數表改變,最常見的方法就是用時間去修改亂
數表產生的亂數
亂數應用(四)
•所以我們可以知道一個正式的亂數需要:
• 調用亂數表 #include<random.h>
• 用時間去避免亂數表固定 #include<time.h>
• 用取餘數的方式去限定其範圍
rand function(一)
•據國際 ISO9899:2011文件中,7.22.2.1 The rand function:
• Synopsis:
• #include <stdlib.h> 引入標頭檔
• int rand(void); 使用方式
• Description:
• The rand function computes a sequence of pseudo-random integers in the
range 0 to RAND_MAX.
• The rand function is not required to avoid data races with other calls to pseudo-
random sequence generation functions. The implementation shall behave as if
no library function calls the rand function.
• 描述 即:
• 其大小範圍位於 0 to RAND_MAX
• 不需要rand函數來避免與其他調用偽隨機序列生成函數的數據競爭。 實現應該像
沒有庫函數調用rand函數一樣。
rand function(二)
•續上頁7.22.2.1 The rand function::
• Returns
• The rand function returns a pseudo-random
• integer.Environmental limits
• The value of the RAND_MAX macro shall be at least 32767.
• 回傳:
• 偽隨機整數(因為該亂數是由亂數表取出的,所以算是偽亂數,接近亂數而非亂數)
• 範圍:
• RAND_MAX巨集定義亂數最大值的底限要在32767以上
rand function(二)
我們可以由此看到RAND_MAX的定義
srand function
•據國際 ISO9899:2011文件中,7.22.2.2 The srand function:
• Synopsis
• #include <stdlib.h> 引入標頭檔
• void srand(unsigned int seed); 使用方式
• Description
• The srand function uses the argument as a seed for a new sequence of pseudo-
random numbers to be returned by subsequent calls to rand. If srand is then
called with the same seed value, the sequence of pseudo-random numbers shall
be repeated. If rand is called before any calls to srand have been made, the
same sequence shall be generated as when srand is first called with a seed
value of 1.
• srand function是用來設定亂數的"種子",當設定srand(種子)時會決定亂數表的資
料;當設定用的"種子"相同時,產生的亂數表資料也會相同
time function(一)
•據國際 ISO9899:2011文件中,7.27.2.4 The time function:
• Synopsis
• #include <time.h> 引入標頭檔
• time_t time(time_t *timer); 使用方式
• Description
• The time function determines the current calendar time. The encoding of the
value is unspecified.
• Returns
• The time function returns the implementation’s best approximation to the
current calendar time. The value (time_t)(-1) is returned if the calendar time is
not available. If timer is not a null pointer, the return value is also assigned to
the object it points to.
time function(二)
•定義上:
• 時間函數確定當前的日曆時間。 該值的編碼是未指定的。
•回傳上:
• 時間函數將實現的最佳近似值返回到當前日曆時間。
如果不用srand(...)函數的程式碼
如果不用srand(...)函數的第一次執行
如果不用srand(...)函數的第二次執行
由此可見再不用srand(...)時
程式會使用固定的亂數表
接著,我們試著加入srabd(固定常數)看看
使用srand(常數)的程式碼
使用srand(常數)的第一次執行
使用srand(常數)的第二次執行
這時亂數表確實會因為srand(固定常數)而改變
但每次程式讀取時srand(固定常數)時
都只會產生固定常數而產生的固定亂數表
於是我們在亂數中加入時間因素
來製作真正可實際應用的亂數
可實際應用亂數程式碼
可實際應用亂數的第一次執行
可實際應用亂數的第二次執行
這樣就能正式使用亂數了
Class 16 記憶體控管
創造一個任意配置大小陣列
•假如我們需要一個陣列,但我們卻不知到其範圍時,就得用動態
陣列去配置.......
實做動態陣列的方法
•當我們要製作動態陣列時,我們就得用記憶體去配置空間
• 首先我們要宣告指標
• 再用malloc取得記憶體空間,並用指標將其實體化
• 實際上就是將指標指向其取得的記憶體空間上
獲取記憶體空間
系統的
記憶體空間
malloc函數請求
給程式的空間
從系統中取得的
記憶體空間
系統的記憶體空間
程式的使用的記憶體空間
原本消耗的
記憶體空間
malloc函數
malloc 函數(一)
•據國際 ISO9899:2011文件中,7.22.3.4 The malloc function:
• Synopsis
• #include <stdlib.h> 引入標頭檔
• void *malloc(size_t size); 呼叫函數
• Description
• The malloc function allocates space for an object whose size is specified by size
and whose value is indeterminate.
• Returns
• The malloc function returns either a null pointer or a pointer to the allocated
space.
malloc 函數(二)
•執行內容
• malloc函數為大小分配大小的空間,其大小是不確定的。
•回傳
• malloc函數返回空指針或指向分配空間的指針。
•也就是我們得要將回傳的空指針(void*)去強制轉型為我們想
要的變數型態,並用該型態去配置動態陣列
釋放記憶體空間
系統的
記憶體空間
歸還的
記憶體空間
從系統中取得的
記憶體空間
系統的記憶體空間
程式的使用的記憶體空間
原本消耗的
記憶體空間
free函數
free 函數(一)
•據國際 ISO9899:2011文件中,7.22.3.3 The free function:
• Synopsis
• #include <stdlib.h> 引入標頭檔
• void free(void *ptr); 呼叫函數
• Description
• The free function causes the space pointed to by ptr to be deallocated, that is,
made available for further allocation. If ptr is a null pointer, no action occurs.
Otherwise, if the argument does not match a pointer earlier returned by a
memory management function, or if the space has been deallocated by a call to
free or realloc, the behavior is undefined.
• Returns
• The free function returns no value.
free 函數(二)
•即:
• 將對該指針(*ptr)的取得的記憶體空間進行釋放
• 如果是空指針的情況下,將會不會進行進行任何操作
用malloc(...)與free(...)實做動態陣列
用malloc(...)與free(...)實做動態陣列
產生20個int Type的陣列
用malloc(...)與free(...)實做動態陣列
產生20個int Type的陣列
Class 17 如何寫個一好的程式碼
首先針對於註解
•還記得在iso9899:1999(C99)後,我們有兩種註解的寫法吧!
•我們可以用這交互使用兩種寫法,來保持註解的易讀性:
• 副程式的大範圍的程式碼註解建議在程式碼上方使用/*......*/的註解
• 而單一行程式碼,則在後面使用//......來註解
對於變數的命名
•變數的命名也是一門重要的學問,建議是能讓下一位維護者能看
懂的命名方式:
• 不要太過草率,如:int a,b,c...;
• 不要太過複雜,如:int Myproject20161224Quit1CodeBool = true;
• 除了迴圈內的區域變數外,建議要遵循內容來命名
• 不要相似或令人稿混的變數命名,如:code、Code、cOde、COde......
• 請盡可能不要使用其他標頭檔內的名稱撞名
針對於二維陣列
•對於二維陣列來說,因為結構略微複雜,也可以寫的明確表以達
出二維陣列的內容:
• int array[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
• int array[3][3] = {
{1, 2, 3} ,
{4, 5, 6} ,
{7 ,8, 9}
};
在自訂函數用指標回傳值時,用void宣告
就如同原始程式:
double Myfunction(int *a,double *b,double c){
//"程式的內容 "
return c;
}
•用void來寫宣告 ,是否看起來比較簡單呢?
void Myfunction(int *a,double *b,double *c){
//"程式的內容 "
}
善用副程式,讓主程式能簡潔
•假如我們所有程式碼都放在主程式時,主程式雖然龐大,但確實
在撰寫時比較輕鬆(不用特別設計)但不容易分析
•但在考慮後續的維護與修改的情況下,撰寫副程式才是比較正確
的方法
善用標頭檔以改善主程式的附程式數量
•當我們做大專案時,不同的程式都要引用你寫好的副程式時,你
可以做出一個標頭檔來放那些副程式,需要用的時候,只要引入
標頭檔就行了
•而引入時,盡可能將自訂的標頭檔與程式內建的標頭檔以不同方
式表示,如:
• #include "MyHear1.h"
• #include "MyHear2.h"
• #include <stdio.h>
• #include <stdlib.h>
語法糖的一致性
•就如同先前所提的程式有語法糖的概念,因為原本的程式碼與語
法糖都會被編譯器解釋相同的內容,而我們在寫程式時,盡可能
不要去混用,應該維持一致性,使程式碼保持整潔
Class 18 資料結構概念說明
基本的資料結構介紹
•陣列(array)
•鍊結串列(Linked List)
•堆疊(Stack)
•佇列(Queue)
•樹(Tree)
陣列(array)
•連續記憶體:
0 1 2 3 4 5 6 7 8 9
結構(struct)
•多種型態組成:
int、char、float、double、struct
*int、*char、*float、*double、*struct
Array[]......
struct
鍊結串列(Linked List)-1
儲存內容 指標
鍊結1
儲存內容 指標
鍊結2
儲存內容 指標
鍊結3
儲存內容 指標
鍊結4
鍊結串列(Linked List)-2
•新增鍊結5
儲存內容 指標
鍊結1
儲存內容 指標
鍊結2
儲存內容 指標
鍊結3
儲存內容 指標
鍊結4
儲存內容 指標
鍊結5
鍊結串列(Linked List)-2
•移除鍊結5
儲存內容 指標
鍊結1
儲存內容 指標
鍊結2
儲存內容 指標
鍊結3
儲存內容 指標
鍊結4
儲存內容 指標
鍊結5
堆疊(Stack) -1
•將資料堆起來,最後存入的資料,將最先被讀取,反之,亦然
資料1
資料2
資料3
資料1
資料2
資料3
資料4
堆疊 堆疊放入新資料
堆疊(Stack) -2
資料1
資料2
資料3
資料1
資料2
資料3資料4
取出資料 再次取出資料
佇列(Queue)
•與堆疊相反,資料先進先出
資料1資料2資料3資料4資料5
存入放向 取出放向
佇列(Queue)-2
•新增資料
資料1資料2資料3資料4資料5
存入放向 取出放向
資料6
佇列(Queue)-3
•取出資料
資料2資料3資料4資料5資料6
存入放向 取出放向
資料1
樹(Tree)
•由上往下長的樹(只有資工系的樹是向下長的):
二元樹
•與樹不同,每筆資料只能向下連結2筆以內(包含2筆)的資料:
二元樹的實際樣貌
儲存內容 指標指標
儲存內容 指標指標儲存內容 指標指標
儲存內容 指標指標 儲存內容 指標指標
Class 19 基礎的演算法
時間複雜度與空間複雜度
•時間複雜度:
• 程式執行所耗費的時間
•空間複雜度
• 程式執行所耗費的記憶體空間
•在理想的情況下:
• 時間複雜度越少越好,花越少的時間
• 空間複雜度越少越好,用最少的空間
演算法探討的事物
•演算法(Algorithm):
• 包含:分治法、動態規劃法、貪婪演算法、線性規劃法、簡併法等......
• 探討問題:排序、搜尋、最短路徑、順序統計、線性規劃等......
常聽到的演算法問題
P問題
•又稱:複雜度(polynomial)
•請參閱:
• https://zh.wikipedia.org/wiki/P_(%E8%A4%87%E9%9B%9C%E5%BA%A6)
PN問題
•又稱:非定常多項式(Non-deterministic polynomial)
•請參閱:
• https://zh.wikipedia.org/wiki/P/NP%E9%97%AE%E9%A2%98
PNC問題
•又稱:NP完全(NP-Complete)
•請參閱:
• https://zh.wikipedia.org/wiki/NP%E5%9B%B0%E9%9A%BE
NP困難
•全名:NP-hard,non-deterministic polynomial-time hard
•因為NP困難問題未必可以在多項式的時間內驗證一個解的正確性
(即不一定是NP問題),因此即使NP完全問題有多項式時間內
的解(若P=NP),NP困難問題依然可能沒有多項式時間內的解。
因此NP困難問題「至少與NPC問題一樣難」。
引用於維基百科 NP困難 條目
P與PN問題
•當一個PN問題無法被證明是否存在一演算法能解時,這演算法可
能存在,但也有可能不存在
•但也無法證明無這種演算法,所以也不能完全說是不存在
•而大多數的人相信這演算法是存在的
Class 19.5 基礎的排序演算法
氣泡排序法(Bubble Sort)
•請參閱:
• https://zh.wikipedia.org/wiki/%E5%86%92%E6%B3%A1%E6%8E%92%
E5%BA%8F
插入排序法(Insertion Sort)
•請參閱:
• https://zh.wikipedia.org/wiki/%E6%8F%92%E5%85%A5%E6%8E%92%
E5%BA%8F
選擇排序法(Selection Sort)
•請參閱:
• https://zh.wikipedia.org/wiki/%E9%80%89%E6%8B%A9%E6%8E%92%
E5%BA%8F
排序法 就實務上而言(一)
•以上四種演算法,實際上算是比較少使用:
• 氣泡排序(bubble sort)— O(n2)
• 插入排序(insertion sort)—O(n2)
• 雞尾酒排序(cocktail sort)—O(n2)
• 桶排序(bucket sort)—O(n);需要O(k)額外空間
• 計數排序(counting sort)—O(n+k);需要O(n+k)額外空間
• 合併排序(merge sort)—O(n log n);需要O(n)額外空間
• 原地合併排序— O(n log2 n)如果使用最佳的現在版本
• 二叉排序樹排序(binary tree sort)— O(n log n)期望時間;O(n2)最壞
時間;需要O(n)額外空間
• 鴿巢排序(pigeonhole sort)—O(n+k);需要O(k)額外空間
• 基數排序(radix sort)—O(n·k);需要O(n)額外空間
引用維基百科 排序算法條目 排序演算法列表之內容
排序法 就實務上而言(二)
• 侏儒排序(gnome sort)— O(n2)
• 圖書館排序(library sort)— O(n log n)期望時間;O(n2)最壞時間;需
要(1+ε)n額外空間
• 塊排序(block sort)— O(n log n)
• 侏儒排序(gnome sort)— O(n2)
• 圖書館排序(library sort)— O(n log n)期望時間;O(n2)最壞時間;需
要(1+ε)n額外空間
• 塊排序(block sort)— O(n log n)
• Bogo排序— O(n × n!),最壞的情況下期望時間為無窮。
• Stupid排序—O(n3);遞迴版本需要O(n2)額外記憶體
• 珠排序(bead sort)— O(n) or O(√n),但需要特別的硬體
• 煎餅排序—O(n),但需要特別的硬體
• 臭皮匠排序(stooge sort)演算法簡單,但需要約n2.7的時間
引用維基百科 排序算法條目 排序演算法列表之內容
排序法 就實務上而言(三)
•我們可以從上述的各算法速度可知比起之前提到的四種演算法外,
還有執行速度更快的演算法
•當然,每種演算法都各有優缺就是了
Class 20 總結
總結(一)
•雖然說C近幾年在程式語言排名上有向下掉的趨勢,但C在歷史
上還是有重要的地位:
•何況現在還有底層驅動與Linux Kernel在用C,C應該還能撐
一段時間
•即便C語言仍有逐漸走下坡的趨勢,但我們不能否定C奠定
的程式語言概念,這也是我們學習C語言重要的原因之一
總結(二)
•也許學完C語言後會學習C++,但要記得:
• 雖然說C++有相容C語言,但兩者是不一樣的
• C就是C,C++就是C++,兩者程式撰寫的實做概念不同
• 請不要用C的概念去直接套用於C++上
• 請記得C函數與C++函數的不同
• C++有運算式重載的概念
• C++有Class,參閱:物件導向(OOP)概念
參考資料的來源
參考資料的來源
•C語言歷史:https://zh.wikipedia.org/wiki/C语言
•部份的C語言資訊:jserv的 "你所部知道的C語言" 系列
•作業系統導論
•ISO9899:2011的相關文件
•維基百科 排序算法與演算法之相關條目
C語言崩潰到崩潰EX(二)到此結束
感謝大家的觀賞
坦白說,這篇做的很趕(半死不活.jpg)

看似比較簡單的推坑教學 C語言從崩潰到崩潰Ex(二)