數組
一、數組的概念
有如下幾組數據:
- 學生的學習成績
- 銀行的賬單
- 一行文字
這些個數據的特點是:
- 具有相同的數據類型;
- 使用過程中需要保留原始數據 。
C語言爲這些數據,提供了一種構造數據類型——數組。
在程序設計中,爲了處理方便,把具有相同類型的若干變量按有序的形式組織起來,這些按序排列的同類數據元素的集合稱爲數組。
在C語言中,數組屬於構造數據類型;
數組元素有序不是指元素大小順序,而是位置順序。
簡而言之,數組就是具有相同類型的數據組成的序列,是一個有序集合。
數組中的每一個數據稱爲數組元素,也稱爲下標變量,即每個元素由其所在的位置序號(稱數組元素的下標)來區分。
用數組名與下標可以用統一的方式來處理數組中的所有元素,從而方便地實現處理一批具有相同性質數據的問題。
一個數組可以分解爲多個數組元素,這些數組元素可以是基本數據類型或是構造類型,因此按數組元素的類型不同,數組又可分爲數值數組、字符數組、指針數組、結構數組等類別。
二、一維數組的定義與引用
1.一維數組定義
在C語言中使用數組必須先進行定義。
一維數組的定義方式爲:
類型說明符 數組名 [常量表達式];
例如int a[10]
定義了一個整型數組,數組名爲a,此數組有10個元素,10個元素都是整型變量。
注意事項
(1)類型說明符是任一種基本數據類型或構造數據類型,對於同一個數組,其所有元素的數據類型都是相同的。
(2)數組名是用戶定義的數組標識符,書寫規則應符合標識符的書寫規定。
(3)方括號中的常量表達式表示數據元素的個數,也稱爲數組的長度。
(4)允許在同一個類型說明中,說明多個數組和多個變量。
例如int a, b, c, d, k1[10], k2[20];
。
(5)a[10]
表示a數組有10個元素,下標從0開始,這10個元素是a[0]、a[1]、…、a[9]。
因此,不存在數組元素a[10]。
(6)C語言不允許對數組的大小作動態定義,即數組的大小不依賴於程序運行過程中變量的值,因爲在編譯的時候就要爲數組預留空間,所以在編寫代碼的時候不能通過變量來定義數組的大小。
例如,下面這樣定義數組是不行的:
int n;
scanf("%d",&n); /* 在程序中臨時輸入數組的大小 */
int a[n];
常見錯誤
float a[0]; /* 數組大小爲0沒有意義 */
int b(2)(3); /* 不能使用圓括號 */
int k, a[k]; /* 不能用變量說明數組大小 */
正確示意如下:
int a[10]; // 聲明整型數組a,有10個元素。
float b[10],c[20]; // 聲明實型數組b,有10個元素,實型數組c,有20個元素。
char ch[20]; // 聲明字符數組ch,有20個元素。
補充–一維數組在內存中的存放
定義一個一維數組int mark[100];
,其在內存中地存放原理如下:
2.一維數組的引用
數組元素是組成數組的基本單元,也是一種變量,其標識方法爲數組名後跟一個下標,下標表示了元素在數組中的順序號。
引用數組元素的一般形式爲數組名[下標]
,下標可以是整型常量或整型表達式。
例如:
a[0] = a[5] + a[7] - a[2*3];
a[i+j];
a[i++];
這些都是合法的數組元素。
注意事項
(1)數組元素通常也稱爲下標變量,必須先定義數組,才能使用下標變量。
在C語言中只能逐個地使用下標變量,而不能一次引用整個數組。
例如,輸出有10個元素的數組必須使用循環語句逐個輸出,示意如下:
#include <stdio.h>
int main(){
int i, a[10];
for(i = 0;i < 10; i++){
a[i] = i;
}
for(i = 9;i >= 0;i--){
printf("%d ", a[i]);
}
return 0;
}
打印:
9 8 7 6 5 4 3 2 1 0
不能用一個語句輸出整個數組,printf("%d",a);
寫法是錯誤的。
(2)定義數組時用到的數組名[常量表達式]
和引用數組元素時用到的數組名[下標]
是有區別的。
例如∶
int a[10]; /* 定義數組長度爲10 */
t = a[6]; /* 引用a數組中序號爲6的元素,此時6不代表數組長度 */
顯然,兩者的含義是不一樣的。
3.一維數組的初始化
給數組賦值的方法除了用賦值語句對數組元素逐個賦值外,還可採用初始化賦值和動態賦值的方法。
數組初始化賦值是指在數組定義時給數組元素賦初值。
數組初始化是在編譯階段進行的,這樣將減少運行間,提高效率;
之前用賦值語句或輸入語句也可給數組素指定初值,是在運行時完成。
初始化賦值
初始化賦值的一般形式爲:
類型說明符 數組名[常量表達式]={值,值,……值};
具體的實現方法有以下幾種:
(1)在定義數組時對數組元素賦以初值。
例如int a[10]= {0,1,2,3,4,5,6,7,8,9};
,將數組元素的初值依次放在一對大括號內。
經過上面的定義和初始化之後,得到a[0] = 0,a[1] = 1,…,a[9] = 9。
測試如下:
#include <stdio.h>
int main(){
int i, a[10] = {0, 1, 2, 3, 4, 5, 6, 7 ,8 ,9};
for(i = 9;i >= 0;i--){
printf("%d ", a[i]);
}
return 0;
}
與之前的效果是一樣的。
(2)可以只給一部分元素賦值。
例如int a[10] = {0,1,2,3,4};
定義a數組有10個元素,但大括號內只提供5個初值,這表示只給前面5個元素賦初值,後5個元素值爲0。
測試如下:
#include <stdio.h>
int main(){
int i, a[10] = {0, 1, 2, 3, 4};
for(i = 9;i >= 0;i--){
printf("%d ", a[i]);
}
return 0;
}
打印:
0 0 0 0 0 4 3 2 1 0
顯然,未定義的元素默認設爲0。
(3)如果想使一個數組中全部元素值爲0,可以寫成int a[10] = {0,0,0,0,0,0,0,0,0,0};
或int a[10] = {0};
。
(4)在對全部數組元素賦初值時,由於數據的個數已經確定,因此可以不指定數組長度。
例如int a[5] = {1,2,3,4,5};
也可以寫成int a[] = {1,2,3,4,5};
。
在第二種寫法中,大括號中有5個數,系統就會據此自動定義a數組的長度爲5。
如果數組長度與提供初值的個數不相同,則數組長度不能省略。
例如,想定義數組長度爲10,就不能省略數組長度的定義,而必須寫成int a[10] = {1,2,3,4,5};
只初始化前5個元素,後5個元素爲0。
數組初始化與未初始化比較測試如下:
#include <stdio.h>
int main(){
int i, a[5] = {3, 4, 5}, b[5];
printf("Array a is:\n");
for(i = 0;i < 5;i++){
printf("%8d", a[i]);
}
printf("\nArray b is:\n");
for(i = 0;i < 5;i++){
printf("%8d", b[i]);
}
return 0;
}
打印:
Array a is:
3 4 5 0 0
Array b is:
-1 -1 4236709 0 1
顯然,b數組未賦值,所以打印出了很亂的值。
動態賦值
動態賦值的方法示例如下:
#include <stdio.h>
int main(){
int i, max, a[10];
printf("Input 10 numbers:\n");
for(i = 0;i < 10;i++){
scanf("%d", &a[i]);
}
max = a[0];
for(i = 1;i < 10;i++){
if(a[i] > max){
max = a[i];
}
}
printf("max=%d", max);
return 0;
}
打印:
Input 10 numbers:
13
46
70
95
73
25
62
78
54
9
max=95
顯然,在輸入10個數給數組賦值後,打印出了最大值。
練習:
利用數組來求解Fibonacci數列前20個數。
代碼如下:
#include <stdio.h>
int main(){
int i;
int a[20] = {1, 1};
for(i = 2;i < 20;i++){
a[i] = a[i - 1] + a[i - 2];
}
for(i = 0;i < 20;i++){
printf("%6d", a[i]);
if(i % 5 == 4){
printf("\n");
}
}
return 0;
}
打印:
1 1 2 3 5
8 13 21 34 55
89 144 233 377 610
987 1597 2584 4181 6765
練習:
用冒泡法(起泡法)對10個數排序(由小到大)。
代碼如下:
#include <stdio.h>
int main(){
int i, j;
int a[10];
for(i = 0;i < 10;i++){
scanf("%d", &a[i]);
}
for(i = 9;i >= 1;i--){
for(j = 0;j < i;j++){
int temp;
if(a[j] > a[j + 1]){
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
printf("The sorted nubers:\n");
for(i = 0;i < 10;i++){
printf("%d ", a[i]);
}
return 0;
}
打印:
12 37 65 43 97 82 120 63 9 17
The sorted nubers:
9 12 17 37 43 63 65 82 97 120
顯然,最後得到的就是已經排好序的數組。
三、二維數組的定義和引用
1.二維數組的定義
二維數組定義的一般形式爲:
類型說明符 數組名[常量表達式][常量表達式];
例如:定義a爲3X4 (3行4列)的數組,b爲5X10(5行10列)的數組,如下:
float a[3][4], b[5][10];
不能寫成
float a[3, 4], b[5, 10];
二維數組可理解爲元素是一維數組的一維數組,例如int a[3][4];
理解如下:
多維數組的定義:
例如float a[2][3][4];
。
二維數組在內存中的存放方式示意如下:
三維數組在內存中的存放方式示意如下:
2.二維數組的引用和初始化
引用數組元素的表示形式:
數組名[下標][下標]
其中,下標可以是整型常量或整型表達式。
如
int a[4][3], i=2, j=1;
a[2][3];
a[i][j];
a[i+1][2*j-1];
但a[i, j]
就是錯誤的。
int a[3][4] = {1, 5, 9}
是給a數組的第一個子數組a[0]的前3個元素賦值,與前者不一樣。
初始化數組的形式爲:
數據類型 數組名[常量表達式1][常量表達式2] = {初始化數據};
有4種方法對二維數組初始化:
(1)直接分行給二維數組賦初值。
如int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
。
(2)可以將所有數據寫在一個大括號內,按數組排列的順序對各元素賦初值。
如int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
。
(3)可以對部分元素賦初值。
如int a[3][4]={{1}, {5}, {9}};
,存放如下:
也可以對各行中的某一元素賦初值。
如int a[3][4]={{1}, {0, 6}, {0, 0, 11}};
,存放如下:
其實也可以只對某幾行元素賦初值。
如int a[3][4]={{1}, {5, 6}};
,存放如下:
(4)如果對全部元素都賦初值,則定義數組時對第一維的長度可以不指定,但第二維的長度不能省。
如,int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
等價於int a[][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
。
在定義時也可以只對部分元素賦初值而省略第一維的長度,但應分行賦初值。
如int a[][4] ={{0, 0, 3}, {}, {0, 10}};
,存放如下:
練習:
如下圖,一個學習小組有5個人,每個人有三門課的考試成績。將各個數據保存到二維數組a[5][3]中,並求全組分科的平均成績和總平均成績。
代碼如下:
#include <stdio.h>
int main(){
int i, j;
double savg[3];
int a[5][3] = {{80, 75, 92}, {61, 65, 71}, {59, 63, 70}, {85, 87 ,90}, {76, 77, 85}};
for(i = 0;i < 3;i++){
int sum = 0;
for(j = 0;j < 5;j++){
sum += a[j][i];
}
savg[i] = sum / 5.0;
}
int tavg = 0;
for(i=0;i<3;i++){
printf("Average Grade:%.2f\n", savg[i]);
tavg += savg[i];
}
printf("Total AVerage Grade is:%.2f", tavg / 3.0);
return 0;
}
打印:
Average Grade:72.20
Average Grade:73.40
Average Grade:81.60
Total AVerage Grade is:75.33
練習:
將一個二維數組行和列元素互換,存到另一個二維數組中。
例如,將數組a[2][3]轉化爲數組b[3][2]如下:
代碼:
#include <stdio.h>
int main(){
int i, j;
int a[5][3] = {{80, 75, 92}, {61, 65, 71}, {59, 63, 70}, {85, 87 ,90}, {76, 77, 85}}, b[3][5];
printf("Array A:\n");
for(i = 0;i < 3;i++){
for(j = 0;j < 5;j++){
printf("%4d", a[j][i]);
b[i][j] = a[j][i];
}
printf("\n");
}
printf("Arary B:\n");
for(i = 0;i < 5;i++){
for(j = 0;j < 3;j++){
printf("%4d", b[j][i]);
}
printf("\n");
}
return 0;
}
打印:
Array A:
80 61 59 85 76
75 65 63 87 77
92 71 70 90 85
Arary B:
80 75 92
61 65 71
59 63 70
85 87 90
76 77 85
練習:
有一個矩陣,要求編程序求出其中最大的元素,以及其所在的行號和列號。
代碼如下:
#include <stdio.h>
int main(){
int i, j, row = 0, column = 0, max = 0;
int a[5][3] = {{80, 75, 92}, {61, 65, 71}, {59, 63, 70}, {85, 87 ,90}, {76, 77, 85}};
max = a[0][0];
for(i = 0;i < 3;i++){
for(j = 0;j < 5;j++){
if(a[j][i] > max){
max = a[j][i];
row = j;
column = i;
}
}
}
printf("Max=%d, in row %d column %d", max, row, column);
return 0;
}
打印:
Max=92, in row 0 column 2
練習:
從鍵盤上輸入9個整數,(對照九宮格的形式,輸入三行,每行輸入三個數) 保存在二維數組中,按數組原來位置輸出第一行和第一列的所有元素。
如果數組如下:
則輸出爲:
代碼如下:
#include <stdio.h>
int main(){
int i, j, a[3][3];
for(i = 0;i<3;i++){
for(j = 0;j<3;j++){
printf("a[%d][%d] = ", i, j);
scanf("%d", &a[i][j]);
}
}
for(i=0;i<3;i++){
for(j=0;j<3;j++){
if(i == 1 || j == 1){
printf("%-6d", a[i][j]);
}
else{
printf("%-6c", ' ');
}
}
printf("\n");
}
return 0;
}
打印:
a[0][0] = 12
a[0][1] = 34
a[0][2] = 56
a[1][0] = 78
a[1][1] = 90
a[1][2] = 98
a[2][0] = 76
a[2][1] = 54
a[2][2] = 43
34
78 90 98
54
四、數組的應用--二分法
利用數組進行數據查找——(二分法)折半查找法:
適應情況:
在一批有序數據中查找某數;
基本思想:
選定這批數中居中間位置的一個數與所查數比較,看是否爲所找之數,若不是,利用數據的有序性,可以決定所找的數是在選定數之前還是在之後,從而很快可以將查找範圍縮小一半。以同樣的方法在選定的區域中進行查找,每次都會將查找範圍縮小一半,從而較快地找到目的數。
練習:
假設在數組a中的數據是按由小到大順序排列的:
-12 0 6 16 23 56 80 100 110 115
。
從鍵盤上輸入一個數,判定該數是否在數組中,若在,輸出所在序號。
實現思路:
- 設low、mid和high三個變量,分別指示數列中的起始元素、中間元素與最後一個元素位置, 其初始值爲low=0,high=9,mid=4,判斷mid指示的數是否爲所求,mid指示的數是23,不是要找的80,須繼續進行查找。
- 確定新的查找區間。因爲80大於23,所以查找範圍可以縮小爲23後面的數,新的查找區間爲[56 80 100 110 115],low,mid,high分別指向新區間的開始、中間與最後一個數。實際上high不變,將low(low=mid+1)指向56,mid (mid=(low+high)/2)指向100,還不是要找的80,仍須繼續查找。
- 上一步中,所找數80比mid指示的100小,可知新的查找區間爲[56 80],low不變,mid與high的值作相應修改。mid指示的數爲56,還要繼續查找。
- 根據上一步的結果,80大於mid指示的數56,可確定新的查找區間爲[80],此時,low與high都指向80,mid亦指向80,即找到了80,到此爲止,查找過程完成。
注意:
若在查找過程中,出現low > high的情況,則說明序列中沒有該數,亦結束查找過程。
代碼如下:
#include <stdio.h>
#define M 10
int main(){
static int a[M] = {-12, 0, 6, 16, 23, 56, 80, 100, 110, 115}; // 定義靜態變量
int n, low = 0, mid, high = M - 1, found = 0;
printf("Input a number to be searched:\n");
scanf("%d", &n);
while(low <= high){
mid = (low + high) / 2;
if( n== a[mid]){
found = 1;
break;
}
else if(n > a[mid]){
low = mid + 1;
}
else{
high = mid - 1;
}
}
if(found == 1){
printf("The number %d is found, and the index is %d\n", n, mid);
}
else{
printf("The number %d is not found\n", n);
}
return 0;
}
打印:
Input a number to be searched:
80
The number 80 is found, and the index is 6
可以看到,在程序中定義了一個靜態變量;
C程序在編譯時,普通變量存放在棧區,static關鍵字會使變量存放在data區。
補充知識——內存分爲四大區:
- code區
寫的代碼存放的地方。 - data區
常量、字符串和static聲明的變量存放的地方,特點是不會改變,整個程序結束之後纔會釋放。 - stack區
普通變量存放的地方,函數調用完成後就會釋放。 - heap區
malloc函數定義,由開發者自己分配。
還可以進一步優化:
如果輸入的數大於最大的數或小於最小的數,說明這個有序序列中不存在要尋找的數,可以直接不用循環查找,改進如下:
#include <stdio.h>
#define M 10
int main(){
static int a[M] = {-12, 0, 6, 16, 23, 56, 80, 100, 110, 115};
int n, low = 0, mid, high = M - 1, found = 0;
printf("Input a number to be searched:\n");
scanf("%d", &n);
if(n < a[0] || n > a[M - 1]){
while(low <= high){
mid = (low + high) / 2;
if( n== a[mid]){
found = 1;
break;
}
else if(n > a[mid]){
low = mid + 1;
}
else{
high = mid - 1;
}
}
if(found == 1){
printf("The number %d is found, and the index is %d\n", n, mid);
}
else{
printf("The number %d is not found\n", n);
}
}
else{
printf("Illegal Input!!!");
}
return 0;
}
打印:
Input a number to be searched:
a
Illegal Input!!!
顯然,此時可以識別非法輸入。
也可以改進如下:
#include <stdio.h>
#define M 10
int main(){
static int a[M] = {-12, 0, 6, 16, 23, 56, 80, 100, 110, 115};
int n, low = 0, mid, high = M - 1, found = 0;
printf("Input a number to be searched:\n");
scanf("%d", &n);
while(scanf("%d", &n) != 1){
printf("Illegal Input!!\nPlease Input Again!!\n");
getchar();
}
while(low <= high){
mid = (low + high) / 2;
if( n== a[mid]){
found = 1;
break;
}
else if(n > a[mid]){
low = mid + 1;
}
else{
high = mid - 1;
}
}
if(found == 1){
printf("The number %d is found, and the index is %d\n", n, mid);
}
else{
printf("The number %d is not found\n", n);
}
return 0;
}
打印:
Input a number to be searched:
a
Illegal Input!!
Please Input Again!!
bc
Illegal Input!!
Please Input Again!!
Illegal Input!!
Please Input Again!!
12
The number 12 is not found
顯然,此時如果輸入有誤,會提示再輸入,直到輸入合法,再向下執行並判斷。