1. 程式人生 > >《演算法筆記》 第2章 C/C++快速入門

《演算法筆記》 第2章 C/C++快速入門

2.1基本資料型別

1.整型(%d)

題目要求10^{9}以內(或者32位整數),就用 int 型;

10^{18}以內(例如10^{10})(或者64位整數),用 long long 型。

2.浮點型(%f)

碰到浮點型的資料都應該用 double 來儲存

3.字元型

(1)字元變數和字元常量(%c)

char c = 'e'

c——字元變數;e——字元常量

(2)字串常量(%s)

char str[10] = "wo ai de ren bu ai wo."

 

2.1.4 符號常量和 const 常量

#define pi 3.14

const double pi 3.14

(巨集定義:直接將對應的部分替換,然後才進行編譯和執行)

#include <stdio.h>
#define CAL(x) (x * 2 + 1)
int main(){
	int a = 1;
	printf("%d\n", CAL(a + 1));
	return 0;
}

CAL(a+1) 實際上是 (a+1*2+1)      ——把替換的部分原封不動替換進去,所以要加必要的括號

2.1.5 運算子

除法運算子:向下取整

自增運算子:++i 先使用 i 再將 i 加 1 ,i++ 先將 i 加 1 再使用 i 

邏輯運算子:&&   ||    !

條件運算子:A ? B : C   如果 A 為真,則返回 B ,如果 A 為假,則返回 C 


2.2 順序結構

2.2.1 賦值表示式(=)

可以將 其他運算子 放在前面來實現賦值操作的簡化。例如:n+=2 —  n = n + 2

2.2.2 scanf 和 printf

scanf:除了 char 陣列整個輸入的情況下不加 & 之外,其他變數型別都需要加 &

三種實用的輸出方式:

%md:使不足 m 位的 int 型變數以 m 位進行右對齊輸出,其中高位用空格補齊;如果變數本身超過 m 位,則保持原樣。

%0md:與 "%md" 唯一不同點,用 0 補齊,而不是空格

%.mf:讓浮點數保留 m 位小數輸出

2.2.3 getchar 和 putchar

getchar:輸入單個字元

putchar:輸出單個字元

2.2.5 typedef(重新命名)

給複雜的資料型別起一個別名:typedef long long LL;

2.2.6 常用 math 函式

1. fabs(double x)——絕對值

2. floor(double x)——向下取整;ceil(double x)——向上取整

3. pow(double r, double p)—— r^{p}

4. sqrt(double x)——\sqrt{x}

5. log(double x)——log_{e}x

6. sin(double x)、cos(double x)、tan(double x)     引數要求是弧度制(用弧長與半徑之比度量對應圓心角角度的方式

例如:double db1 = sin(pi * 45 / 180);   輸出結果:0.707107

7. asin(double x)、acos(double x)、atan(double x)     返回 反正弦值、反餘弦值、反正切值

例如:double db1 = asin(1);  輸出結果:1.570796

8. round(double x)   四捨五入,返回也是double,需要強制轉換取整

2.3 選擇結構

2.3.1 if語句

技巧:如果表示式是 "!=0" 則可以省略 "!=0" ;if(n) —— if(n != 0)

           如果表示式是 "==0" 則可以省略 "==0";if(!n) —— if(n == 0)

2.4 迴圈結構

do...while 和 while 的不同:

do...whiile 會先執行迴圈體一次,然後才去判斷迴圈條件是否為真。

while 只要條件成立,就反覆執行

2.4.4 break  和 continue 

break:強制退出 switch 語句

continue:臨時結束迴圈的當前輪迴,然後進入下一個輪迴


2.5 陣列

【遞推】

根據一些條件,可以不斷讓後一位的結果由前一位或前若干位計算得來。分為 順推 和 逆推。

2.5.2 氣泡排序

本質:交換,通過每次交換的方式把當前剩餘元素的最大值移動到一端,當剩餘元素減少為0時,排序結束

#include <stdio.h>
int main(){
    int a[10] = {3, 1, 4, 5, 2};
    for(int i = 1; i <= 4; i++){    // 進行 n-1 趟
        for(int j = 0; j < 5-i; j++){    // 第i趟從 a[0] 到 a[n-i-1] 都與它們下一個數比較
            if(a[j] > a[j+1]){
                int temp = a[j];
                a[j] = a[j+1];
                a[j+1] = temp;    
            }
        }           
    }
}

2.5.3 二維陣列

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

輸出結果:

3 1 2 0 0 0    8 4 0 0 0 0 ...

notes:

如果陣列大小較大(大概10^{6}級別),則需要定義在函式外面。(原因:函式內部申請的區域性變數來自系統棧,允許的空間較小;而函式外部申請的全域性變數來自靜態儲存區,允許申請的空間較大)

2.5.4 memset —— 對陣列中的每一個元素賦相同的值

只賦值 0 或 -1,如果賦其他數字(例如1),用 fill 函式

函式格式:memset(陣列名,值,sizeof(陣列名))   例如:memset(a, 0, sizeof(a))

2.5.5 字元陣列

2. 字元陣列的輸入輸出

(1) scanf,  printf

     scanf:%c 用來輸入單個字元,%s (不用加 & )用來輸入一個字串並存在字元陣列。%c 格式能識別空格跟換行,並將其輸入。%s 識別空格作為字串的結尾。

(2) getchar, putchar  分別用來輸入和輸出 單個字元

(3) gets、puts 

      gets:用來輸入一行字串(識別換行符 "\n" 作為輸入結束),scanf 完一個整數後,如果要使用 gets ,則需要先用getchar接收整數後的換行符

3. 字元陣列的存放方式

    字元陣列的末尾都有一個 空字元 '\0'  以表示存放的字串的結尾。

notes:在開字元陣列時記得長度一定要比實際儲存字串的長度至少多 1 ,int 型陣列不需要,只有 char 型陣列需要。

             如果不是使用 scanf 函式的 %s 格式 或 gets 函式輸入字串(例如使用 getchar ),一定要在輸入的每個字串後加入 '\0' , 否則 printf 和 puts 輸出字串時會因為無法識別字符串末尾而輸出一大堆亂碼。

2.5.6  string.h 標頭檔案 包含的一些 用於 字元陣列的 函式

1. strlen()——得到字元陣列第一個 '\0' 前的字元個數,格式:strlen(字元陣列)

2. strcmp()——返回兩個字串大小的比較結果,比較原則:字典序(類似於查英文詞典,aaaa就在aab之前)。格式:strcmp(字元陣列1,字元陣列2),返回結果:< 負整數;== 0;> 正整數

3. strcpy()—— 格式:strcpy(字元陣列1,字元陣列2)   把 2 複製 給 1,包含結束符 '\0'

4. strcat()—— 格式:strcat(字元陣列1,字元陣列2) 把 2 接到 1 後面

2.5.7 sscanf  sprintf

sscanf(str, "%d", &n)  \rightarrow 把字元陣列 str 中的內容以 "%d" 的格式寫到 n 中 

sprintf(str, "%d", n)     \leftarrow 把 n 以 "%d" 的格式寫到 str 字元陣列中


2.6 函式

無參函式、有參函式

全域性變數、區域性變數

值傳遞

#include <stdio.h>
void change(int x){
	x = x + 1;
}
int main(){
	int x = 10;
	change(x);
	printf("%d\n", x);
	return 0;
}

因為 change 函式的引數 x 為區域性變數,僅在函式內部生效,通過 change(x) 傳進去的 x 只是傳進去的一個副本,也即 change 函式的引數 x 和 main 函式裡的 x 是作用於兩個不同函式的不同變數。這種引數傳遞的方式稱為 值傳遞。

地址傳遞

指標型別也可以作為函式引數的型別,這時視為把變數的地址傳入函式。如果在函式中對這個地址中的元素進行改變,原先的資料就會確實地被改變。

#include <stdio.h>
void swap(int *a, int *b){
	int temp = *a;
	*a = *b;
	*b = temp;
}
int main(){
	int m = 3, n = 2;
	int *p = &m, *q = &n;
	swap(p, q);
	printf("%d %d", m, n);
	return 0;
}

錯誤寫法2:

void swap(int* a, int* b){
	int *temp = a;
	a = b;
	b = temp;
}

思想:直接把兩個地址交換。swap 函式裡交換完地址之後 main 函式裡地 a 與 b 的地址也被交換。函式引數的傳送方式是單向一次性的, main 函式傳給 swap 函式的“地址”其實是一個“無符號整型”的數,其本身也跟普通變數一樣只是“值傳遞”,swap 函式對地址本身進行修改並不能對 main 函式裡的地址修改,能夠使 main 函式裡的資料發生變化的只能是 swap 函式中對地址指向的資料進行的修改。對地址本身進行修改其實跟之前對傳入的普通變數進行交換的函式是一樣的作用,都只是副本,沒法對資料進行實質性的影響,即相當於把 int* 看作一個整體,傳入的 a 和 b 都只是地址的副本。

2.6.3 以陣列作為函式引數

陣列作為引數時,在函式中對 陣列元素的修改 就等同於是 對原陣列元素地修改

(引數中 陣列的第一維不需要填寫長度,如果是二維陣列,那麼第二維需要填寫長度)

#include <stdio.h>
void change(int a[], int b[][5]){
	a[0] = 1;
	a[1] = 3;
	a[2] = 5;
	b[0][0] = 1;
}
int main(){
	int a[3] = {0};
	int b[5][5] = {0};
	change(a, b);
	for(int i = 0; i < 3; i++){
		printf("%d\n", a[i]);
	}
	return 0;
}

2.6.4 函式的巢狀呼叫

2.6.5 函式的遞迴呼叫(函式自己呼叫自己)

#include <stdio.h>
int F(int n){
	if(n == 0)
		return 1;
	else
		return F(n-1) * n;
}
int main(){
	int n;
	scanf("%d", &n);
	printf("%d\n", F(n));
	return 0;
}

2.7 指標

在計算機中,每個位元組(即房間)都會有一個地址(即房間號),而計算機就是通過地址找到某個變數的。

指標,就是變數的地址,在變數前面加上 '&',就表示變數的地址。

2.7.2 指標變數

int* p     int* 是指標變數的型別,p 才是指標變數名,( * 是型別的一部分)

給指標變數賦值的方式 就是 把變數的地址取出來,然後賦給對應型別的指標變數。例:int* p = &a;

如何得到這個地址所指的元素:

把 * 視為一把開啟房間的鑰匙,將其加在 p 的前面,例:

        int* p = &a;   printf("%d", *p);

指標變數支援加減法,減法的結果就是兩個地址偏移的距離。對一個 int* 型的變數來說, p+1 就是 p 所指的 int 型變數的下一個 int 型變數地址。

2.7.3 指標和陣列

陣列名稱可以作為陣列的首地址,而 a[0]的首地址為 &a[0],即 a = &a[0],推出 a+i = &a[i]

2.7.4 使用指標變數作為函式引數

把 變數的地址 傳入函式,如果在函式中對 這個地址中的元素 進行改變,原先的資料 就會確實地被改變。例如:(地址傳遞)

#include <stdio.h>
void change(int* p){
	*p = 233;
}
int main(){
	int n = 1;
	int* m = &n;
	change(m);
	printf("%d", n);
	return 0;
}

2.7.5 引用

1. 引用的含義

不使用指標,達到修改傳入引數的目的。引用不產生副本,而是給原變數起了個別名。引用變數的操作就是對原變數的操作。只需要在函式的引數型別後面加個 & 就可以。例如:

#include <stdio.h>
void change(int &a){
	a = 1;
}
int main(){
	int m = 5;
	change(m);
	printf("%d", m);
	return 0;
}

2. 指標的引用

對指標變數本身的修改 無法作用到 原指標變數上。

#include <stdio.h>
void swap(int* &p1, int* &p2){
	int* temp = p1;
	p1 = p2;
	p2 = temp;
}
int main(){
	int a = 2, b = 1;
	int* p = &a;
	int* q = &b;
	swap(p, q);
	printf("%d %d", *p, *q);
	return 0;
}

2.8 結構體的使用


2.9 補充

2.9.1  cin 與 cout

需要新增標頭檔案 #include <iostream>  和 "using namespace std;"   不需要取地址運算子 &

2.9.2 浮點數的比較

2.9.3 複雜度

 

2.10 黑盒測試

1. 單點測試

2. 多點測試