第六章:利用數組處理批量數據
1. 用篩選法求100之內的素數
【答案解析】
素數:約數為1和該數本身的數字稱為素數,即質數
篩選法:又稱為篩法。先把N個自然數按次序排列起來。1不是質數,也不是合數,要划去。第二個數2是質數留下來,而把2后面所有能被2整除的數都划去。2后面第一個沒划去的數是3,把3留下,再把3后面所有能被3整除的數都划去。3后面第一個沒划去的數是5,把5留下,再把5后面所有能被5整除的數都划去。這樣一直做下去,就會把不超過N的全部合數都篩掉,留下的就是不超過N的全部質數。因為希臘人是把數寫在塗臘的板上,每要划去一個數,就在上面記以小點,尋求質數的工作完畢后,這許多小點就像一個篩子,所以就把埃拉托斯特尼的方法叫做“埃拉托斯特尼篩”,簡稱“篩法”。(另一種解釋是當時的數寫在紙草上,每要划去一個數,就把這個數挖去,尋求質數的工作完畢后,這許多小洞就像一個篩子。)
【代碼實現】
//用篩選法求100以內的素數
#include<stdio.h>
int main()
{
int i, j, k = 0;
// 將數組匯總每個元素設置為:1~100
int a[100];
for (i = 0; i < 100; i++)
a[i] = i+1;
// 因為1不是素數,把a[0]用0標記
// 最后一個位置數字是100,100不是素數,因此循環可以少循環一次
a[0] = 0;
for (i = 0; i < 99; i++)
{
// 用a[i]位置的數字去模i位置之后的所有數據
// 如果能夠整除則一定不是素數,該位置數據用0填充
for (j = i + 1; j < 100; j++)
{
if (a[i] != 0 && a[j] != 0)
{
//把不是素數的都賦值為0
if (a[j] % a[i] == 0)
a[j] = 0;
}
}
}
printf(" 篩選法求出100以內的素數為:\n");
for (i = 0; i < 100; i++)
{
//數組中不為0的數即為素數
if (a[i] != 0)
printf("%3d", a[i]);
}
printf("\n");
return 0;
}
【運行結果】
2. 用選擇法對10個整數排序
【答案解析】
選擇排序原理:
總共兩個循環,外循環控制選擇的趟數,內循環控制具體選擇的方式。
用maxPos標記區間中首元素位置,然后用后序元素依次與maxPos標記的元素進行比較,如果有元素大於maxPos位置的元素,用maxPos標記該元素的位置,直到區間的末尾。
該趟選擇完成后,即找到該區間中最大元素,如果maxPos標記的最大元素不在區間末尾,用maxPos位置元素與區間末尾的元素進行交換。
繼續新一趟選擇,直到區間中剩余一個元素
【代碼實現】
#include<stdio.h>
int main()
{
int array[] = {2,8,3,9,5,7,1,4,0,6};
int size = sizeof(array) / sizeof(array[0]);
// 輸出原數組
printf("排序前數組中數據為:");
for (int i = 0; i < size; ++i)
printf("%d ", array[i]);
printf("\n");
// 選擇排序過程:
// 外循環控制選擇的趟數,總共選擇size-1趟,
// 減1是因為最后一趟選擇區間中剩余一個元素,該趟選擇可以忽略
for (int i = 0; i < size-1; ++i)
{
// 用maxPos標記[0, size-i)區間中最大元素
// 在該趟選擇沒有開始前,默認認為0號位置就是最大元素
int maxPos = 0;
for (int j = 1; j < size - i; ++j)
{
// 遍歷區間[0, size-i)中元素,如果有元素比maxPos位置元素大,maxPos記錄該元素位置
if (array[j] > array[maxPos])
maxPos = j;
}
// 如果最大元素不在區間末尾時,將最大元素與區間末尾元素交換
if (maxPos != size - i - 1)
{
int temp = array[maxPos];
array[maxPos] = array[size - i - 1];
array[size - i - 1] = temp;
}
}
// 輸出原數組
printf("選擇排序后數組中數據為:");
for (int i = 0; i < size; ++i)
printf("%d ", array[i]);
printf("\n");
return 0;
}
【結果截屏】
優化:既然一趟選擇能找到最大的元素,那么也可以找到最小的元素,因此在一趟中可以找到最小和最大兩個元素,最小元素放在區間左側,最大元素放在區間右側,可以減少選擇的趟數。
#include<stdio.h>
int main()
{
int array[] = {2,8,3,9,5,7,1,4,0,6};
int size = sizeof(array) / sizeof(array[0]);
// 輸出原數組
printf("排序前數組中數據為:");
for (int i = 0; i < size; ++i)
printf("%d ", array[i]);
printf("\n");
int begin = 0, end = size - 1;
// [begin, end]區間中進行選擇
while (begin < end)
{
int maxPos = begin; // 標記區間中最大元素的位置
int minPos = begin; // 標記區間中最小元素的位置
int index = begin + 1;
while (index <= end)
{
if (array[index] > array[maxPos])
maxPos = index;
if (array[index] < array[minPos])
minPos = index;
++index;
}
// 如果最大元素不在區間末尾,則交換
if (maxPos != end)
{
int temp = array[maxPos];
array[maxPos] = array[end];
array[end] = temp;
}
// 如果在交換前區間末尾剛好存儲的是最小的元素,則最小的元素被交換到maxPos位置
// 此時需要更新minPos
if (minPos == end)
minPos = maxPos;
// 如果最小元素不在區間起始位置,則交換
if (minPos != begin)
{
int temp = array[minPos];
array[minPos] = array[begin];
array[begin] = temp;
}
// 最大與最小元素已經在區間的起始和末尾的位置,
// 因此begin往后移動,end往前移動
begin++;
end--;
}
// 輸出原數組
printf("選擇排序后數組中數據為:");
for (int i = 0; i < size; ++i)
printf("%d ", array[i]);
printf("\n");
return 0;
}
3. 求一個3 X 3的整形矩陣對角線元素之和
【答案解析】
矩陣:即二維數組,矩陣行和列相等的二維數組稱為方陣。
1 2 3
4 5 6
7 8 9
左上角到右下角對角線上數字:行下標和列下標相等
右上角到左下角對角線上數字:列下標減1 行下標加一
通過兩個循環來取到對角線上的元素,並對其求和即可。
【代碼實現】
#include<stdio.h>
int main()
{
int array[3][3];
int sumLT2RB = 0; // 標記左上角到右下角對角線元素之和
int sumRT2LB = 0; // 標記右上角到左下角對角線元素之和
printf("請輸入3行3列的矩陣:\n");
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
scanf("%d", &array[i][j]);
}
// 左上角到右下角對角線
for (int i = 0; i < 3; ++i)
sumLT2RB += array[i][i];
for (int i = 0, j = 2; i < 3; ++i, j--)
sumRT2LB += array[i][j];
printf("左上角到右下角對角線元素之和: %d\n", sumLT2RB);
printf("右上角到左下角對角線元素之和: %d\n", sumRT2LB);
return 0;
}
【結果截屏】
4. 有一個已經排好序的數組,要求輸入一個數后,按原來順序的規律將它插入數組中
【答案解析】
將數據插入到已排序的序列中,只需兩步即可:
- 找待插入元素在數組中的插入位置
- 插入元素
具體如下:
注意:極端情況下,end可能會被減為-1,比如在上述序列中插入0
【代碼實現】
#include<stdio.h>
int main()
{
int num = 0;
int array[10] = {1,2,3,4,5,6,7,8,9};
printf("請輸入一個整數:");
scanf("%d", &num);
printf("原數組序列為:\n");
for (int i = 0; i < 9; ++i)
printf("%d ", array[i]);
printf("\n");
// 數組為升序
// 在數組中找待插入元素的位置,具體找的方式為:
// 從后往前依次與數組中元素進行比較,如果要插入元素num比end位置數據小,則num一定插在end位置之前
// 因此將end位置數據往后搬移一個位置
// 如果num大於end位置元素或者end已經在區間最左側,則位置找到
// 最后將新元素插入到end+1的位置
int end = 8;
while (end >= 0 && num < array[end])
{
array[end+1] = array[end];
end--;
}
array[end + 1] = num;
printf("插入元素%d之后的結果為:\n", num);
for (int i = 0; i < 10; ++i)
printf("%d ", array[i]);
printf("\n");
return 0;
}
【結果截圖】
5. 將一個數組中的值按逆序重新存放。例如:原來順序為8,6,5,4,1。要求改為1,4,5,6,8。
【答案解析】
該題為數組的逆置,具體處理方式如下:
如果begin < end時,則循環進行一下操作
- 給定兩個下標begin和end,begin放在數組起始的位置,end放在數組最后一個元素的位置
- 交換begin和end位置的元素
- begin往后移動,end往前移動
【代碼實現】
#include<stdio.h>
int main()
{
int array[5] = {8,6,5,4,1};
int begin = 0, end = 4;
printf("逆序之前數組為:");
for (int i = 0; i < 5; ++i)
printf("%d ", array[i]);
printf("\n");
// 逆序:begin在數組最左側,end在數組最右側
// 只要begin < end,將begin和end位置元素進行交換
// 然后begin往后移動一步,end往前移動一步
while (begin < end)
{
int temp = array[begin];
array[begin] = array[end];
array[end] = temp;
begin++;
end--;
}
printf("逆置之后數組為:");
for (int i = 0; i < 5; ++i)
printf("%d ", array[i]);
printf("\n");
return 0;
}
【結果截屏】
6. 輸出一下的楊慧三角(要求輸出10行)
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
……
【答案解析】
仔細觀察楊慧三角可以看到:
第0列和對角線上的數據全部為1,其余位置上的數據為上一行正對數據與上一行正對前一個數據之和。
比如:a[4][2] = a[3][2] + a[3][1]
【代碼實現】
#include<stdio.h>
int main()
{
int array[10][10];
for (int i = 0; i < 10; ++i)
{
for (int j = 0; j <= i; ++j)
{
// 對角線和第0列上全部為1
if (i == j || 0 == j)
array[i][j] = 1;
else
array[i][j] = array[i - 1][j] + array[i - 1][j - 1];
}
}
// 打印楊慧三角的前10行
for (int i = 0; i < 10; ++i)
{
for (int j = 0; j <= i; ++j)
{
printf("%5d", array[i][j]);
}
printf("\n");
}
return 0;
}
【結果截屏】
7. 輸出"魔方陣"。所謂魔方陣是指這樣的方陣,它的每一行、每一列和對角線之和均相等。例如:
8 1 6
3 5 7
4 9 2
要求輸出1~$n^2$的自然數構成的魔方陣。
【答案解析】
| 17 | 24 | 1 | 8 | 15 |
--------------------------
| 23 | 5 | 7 | 14 | 16 |
--------------------------
| 4 | 6 | 13 | 20 | 22 |
--------------------------
| 10 | 12 | 19 | 21 | 3 |
--------------------------
| 11 | 18 | 25 | 2 | 9 |
仔細觀察上述矩陣,可以看到以下規律:
魔方陣的生成方法為:在第0行中間置1,對從2開始的其余$n^2-1$個數依次按下列規則存放:
- 將1放在第1行的中間一列。
- 從2開始直到n*n止,各數依次按此規律存放:每一個數存放的行比前一個數的行數減1,列數加1。
- 如果上一行的行數為1,則下一個數的行數為n(指最下一行)。
- 當上一個數的列數為n時,下一個數的列數應該為1。
- 如果按上面規律確定的位置有數,或者上一個數是第1行第n列時,則把下一個數放在上一個數的下面。
【代碼實現】
#include <stdio.h>
int main()
{
int a[15][15], n, i, j, k;
while (1)
{
printf("請輸入n(1~15):");
scanf("%d", &n);
if (n != 0 && n <= 15 && n % 2 != 0)
break;
else
{
printf("請輸入奇數\n");
}
}
for (i = 1; i <= n; i++)
{
for (j = 1; j <= n; j++)
a[i][j] = 0;
}
j = n / 2 + 1;
a[1][j] = 1;
i = 1;
for (k = 2; k <= n*n; k++)
{
i -= 1;
j += 1;
if (i<1 && j>n)
{
i += 2;
j -= 1;
}
else if (i<1)
{
i = n;
}
else if (j>n)
{
j = 1;
}
if (a[i][j] == 0)
{
a[i][j] = k;
}
else
{
i += 2;
j -= 1;
a[i][j] = k;
}
}
for (i = 1; i <= n; i++)
{
for (j = 1; j <= n; j++)
printf("%5d", a[i][j]);
printf("\n");
}
return 0;
}
【結果截屏】
8. 找出一個二維數組中的鞍點,即該位置上的元素在該行上最大,在該列上最小,也可能沒有鞍點。
【答案解析】
鞍點是行上最大,列上最小的元素,因此對數組的第i元素進行如下操作:
- 找到該行上最大元素,用max標記,並標記該元素所在列colindex
- 找colindex列上最小的元素,用min標記,並標記該元素所在行號rowindex
- 如果max和min相等,並且最小的元素剛好是在第i行,則為鞍點
如果所有行找完了,沒有輸出則沒有鞍點
【代碼實現】
#include<stdio.h>
#define M 3
#define N 4
int main()
{
int max, min, rowindex, colindex, flag = 0;
int array[M][N];
printf("請輸入%d行%d列的數組:\n", M, N);
for (int i = 0; i < M; i++)
{
for (int j = 0; j < N; j++)
scanf("%d", &array[i][j]);
}
for (int i = 0; i < M; ++i)
{
// 找到i行上最大的元素,記錄該元素在列號colindex
max = array[i][0];
for (int j = 0; j < N; ++j)
{
if (array[i][j] > max)
{
max = array[i][j];
colindex = j;
}
}
// 找max所在列colindex上最小的元素,並記錄其所在的行
min = array[0][colindex];
for (int j = 0; j < M; ++j)
{
if (array[j][colindex] < min)
{
min = array[j][colindex];
rowindex = j;
}
}
// 如果最小元素與最小元素相同,並且最小元素也在第i行,則為鞍點
if (max == min && i == rowindex)
{
flag = 1;
printf("鞍點為:%d行%d列的元素%d", rowindex, colindex, max);
break;
}
}
if (0 == flag)
printf("沒有鞍點");
return 0;
}
【結果截屏】
9. 有15個數按由大到小順序存放在一個數組中,輸入一個數,要求用折半查找法找出該數是數組中第幾個元素的值。如果該數不在數組中,則輸出"無此數"。
【答案解析】
二分查找是一個非常高效簡單的查找算法,筆試和面試中非常喜歡考察。
折半查找又叫二分查找,查找的前提是序列中元素必須有序,假設區間使用[left, right)標記,待查找元素為key,具體查找的方式如下:當區間[left, right)有效時循環進行一下操作
- 找到[left, right)區間中間位置
- 如果key等於中間位置元素,則找到,返回該元素在數組中的下標
- 如果key小於中間位置元素,到數組的左半側繼續二分查找
- 如果key大於中間位置元素,到數組的右半側繼續二分查找
如果循環結束時還沒有找到,則不存在該元素。
【代碼實現】
#include<stdio.h>
int main()
{
int array[15] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14};
int left = 0;
int right = sizeof(array) / sizeof(array[0]);
int key = 0;
printf("請輸入要查找的數字: ");
scanf("%d", &key);
// 二分查找
while (left < right)
{
// 找到中間位置
int mid = left + ((right - left) >> 1);
if (key == array[mid])
{
printf("%d\n", mid);
break;
}
else if (key < array[mid])
{
right = mid;
}
else
{
left = mid + 1;
}
}
if (left >= right)
printf("無此數\n");
return 0;
}
【結果截屏】
假設數組中元素為:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
10. 有一篇文章,共有3行文字,每行有80個字符。要求分別統計出其中英文大寫字母、小寫字母、數字、空格以及其他字符的個數。
【答案解析】
獲取文章中的3行文本,並對每行文本進行以下操作
- 定義保存結果變量:upp、low、digit、space、other
- 遍歷每行文本中的字符
- 如果該字符ch:ch >= 'a' && ch <='z',則該字符是小寫字母,給low++
- 如果該字符ch:ch >= 'A' && ch <='Z',則該字符是小寫字母,給up++
- 如果該字符ch:ch >= '0' && ch <='9',則該字符是小寫字母,給digit++
- 如果該字符ch:ch == ' ',則該字符是小寫字母,給space++
- 否則為其他字符,給other++
輸入統計結果
【代碼實現】
#include <stdio.h>
int main()
{
int upp = 0, low = 0, digit = 0, space = 0, other = 0;
char text[3][80];
for (int i=0; i<3; i++)
{
// 獲取一行文本
printf("please input line %d:\n",i+1);
gets(text[i]);
// 統計該行文本中小寫字母、大寫字母、數字、空格、其他字符的個數
for (int j=0; j<80 && text[i][j]!='\0'; j++)
{
if (text[i][j]>='A'&& text[i][j]<='Z') // 大寫字母
upp++;
else if (text[i][j]>='a' && text[i][j]<='z') // 小寫字母
low++;
else if (text[i][j]>='0' && text[i][j]<='9') // 數字
digit++;
else if (text[i][j]==' ') // 控制
space++;
else
other++; // 其他字符
}
}
printf("\nupper case: %d\n", upp);
printf("lower case: %d\n", low);
printf("digit : %d\n", digit);
printf("space : %d\n", space);
printf("other : %d\n", other);
return 0;
}
【結果截屏】
11. 輸出一下圖案:
* * * *
* * * *
* * * *
* * * *
* * * *
【答案解析】
該題非常簡單,觀察圖形可以看出:
- 每行中*的個數相等,都是4個
- 每行中空格的個數在遞增2個
- 每行先輸出空格,然后輸出*
按照以上方式通過循環來控制每行的輸出內容即可
【代碼實現】
#include<stdio.h>
int main()
{
for (int i = 0; i < 5; ++i)
{
// 輸出空格
for (int j = 0; j < i; ++j)
printf(" ");
// 輸出*
printf("* * * *\n");
}
return 0;
}
【結果截屏】
12. 有一行電文,以按下面規律譯成密碼:
A--->Z a--->z
B--->Y b--->Y
C--->X c--->x
……
即第1個字母編程第26個字母,第i個字母編程第(26-i+1)個字母,非字母字符不變,要求編程序將密碼譯回原文,並輸出密碼和原文。
【答案解析】
從題目給的實例中可以看到,編碼規則非常簡單,就是將從前往后數的第i個字母轉化為從后往前數的第i個字母。
那解壓時直接反過來轉換即可:
即'Z'--->'A' 'z'--->'a'
'Y'--->'B' 'y'--->'b'
'X'--->'C' 'x'--->'c'
假設如果當前拿到的是小寫字母,轉換方式如下:
- 先用s[i] - 'a'計算出s[i]是26個字母中從前往后數的第幾個
- 再用26 - (s[i]- 'a') - 1 轉換為26個字母中從后往前數的第幾個
- 在2的結果上加上'a',即轉換為對應從后往前的第幾個字母
大寫字母轉換方式與上述相同,將上述每條中的'a'換為‘A’即可。
【代碼實現】
#include<stdio.h>
int main()
{
char s[1024] = {0};
scanf("%s", s);
int len = strlen(s);
// 轉換
for (int i = 0; i < len; ++i)
{
// 如果是小寫字母(大寫字母出來類似):
// 1. 先用s[i] - 'a'計算出s[i]是26個字母中從前往后數的第幾個
// 2. 再用26 - (s[i]- 'a') - 1 轉換為26個字母中從后往前數的第幾個
// 3. 在2的結果上加上'a',即轉換為對應從后往前的第幾個字母
if (s[i] >= 'a' && s[i] <= 'z')
s[i] = 'a' + 26 - (s[i]-'a')-1;
else if (s[i] >= 'A' && s[i] <= 'Z')
s[i] = 'A' + 26 - (s[i] - 'A')-1;
}
printf("%s", s);
return 0;
}
【結果截屏】
13. 編一程序,將兩個字符串連接起來,不要用strcat函數
【答案解析】
直接將s2中的字符逐個拷貝到s1的末尾即可,用戶需要保證s1中能存的下s2中的字符
- 獲取s1末尾的位置
- 將s2中的字符逐個拷貝到s1中
【代碼實現】
#include<stdio.h>
int main()
{
char s1[100] = {0};
char s2[50] = { 0 };
int index1 = 0, index2 = 0;
printf("請輸入字符串s1:");
scanf("%s", s1);
printf("請輸入字符串s2:");
scanf("%s", s2);
printf("將s2拼接在s1之后: ");
// 1. 找到s1的末尾
while ('\0' != s1[index1])
index1++;
// 2. 將s2中的字符逐個往s1之后拼接
while (s1[index1++] = s2[index2++]);
printf("%s\n", s1);
return 0;
}
【結果截屏】
14. 編寫一個程序,將連個字符串s1和s2比較,如果s1 > s2,輸出一個整數;若s1 = s2,輸出0;若s1 < s2,輸出一個負數。不要用strcpy函數。兩個字符串用gets函數讀入。輸出的正數或負數的絕對值應是相比較的兩個字符串相對應字符的ASCII碼的差值。例如,"A"和“C”相比,由於"A" < "C",應輸出負數,同時由於‘A’與‘C’的ASCII碼差值為2,因此應輸出"-2"。同理:“And”和"Aid"相比較,根據第2個字符比較結果,"n"比"i"大5,因此應輸出"5"。
【答案解析】
字符串比較規則:從前往后逐個字符進行比較,相等時繼續往后,不相等時返回該位置兩個字符差值。
【代碼實現】
#include <stdio.h>
int main()
{
int ret = 0;
int index = 0;
char s1[100] = { 0 };
char s2[100] = { 0 };
printf("請輸入s1:");
gets(s1);
printf("請輸入s2:");
gets(s2);
// 將s1和s2中的字符從前往后逐個進行比較,相等繼續往后,
// 不相等時ret中結果不為0,!ret則為0 循環結束
// 如果一個走到末尾,一個未走到末尾 ret也不為0, !ret為0,循環結束
// 如果兩個字符串相等,同時達到末尾,循環結束
while (!(ret = s1[index] - s2[index]) && '\0' != s1[index] && '\0' != s2[index])
{
++index;
}
printf("%d\n", ret);
return 0;
}
【結果截屏】
15. 編寫一個程序,將字符數組s2中的全部字符復制到字符數組s1中,不用strcpy函數。復制時,‘\0’也要賦值過去。'\0'之后的字符不復制。
【答案解析】
首先必須保證s1能否放的下s2中的字符,然后將s2中的每個字符逐個搬移到s1中即可。
【代碼實現】
#include<stdio.h>
int main()
{
char s1[100] = { 0 };
char s2[50] = { 0 };
int index1 = 0, index2 = 0;
printf("請輸入字符串s2:");
scanf("%s", s2);
printf("將s2拷貝到s1中, s1現在為: ");
// 將s2[index2]位置字符拷貝到s1[index]位置,
// 然后以s1[index1]的值作為循環條件判斷是否拷貝到s2的末尾
while (s1[index1++] = s2[index2++]);
printf("%s\n", s1);
return 0;
}
【結果截屏】