4. 使用数组制作表格
程序的数据结构反映了在实际应用领域中数据的组织形式。
一般来说,只要应用中包含的数据能表示成以下列表的形式:
a
0
,
a
1
,
a
2
,
a
3
,
a
4
,
.
.
.
,
a
N
−
1
a_0,a_1,a_2,a_3,a_4,...,a_{N-1}
a0,a1,a2,a3,a4,...,aN−1
就可以选择数组来表示这组数据。
- 下角标(subscript)
一般会把数组元素的下标称作下角标。
然而,对某些应用来说,不是在数组的连续元素中存放数值,而是用数据生成数组下标值。
这些下标值被用来选择数组中的元素,记录数据的某些统计特性。
例如,假设要写一个程序读入用户输入的文本行,并记录26个英文字母出现的次数。
当用户输入一个空行表示输入结束的时候,程序应该输出一个表,说明在输入数据中各个字母出现的次数。
运行示例如下:
这个程序值得注意的地方是:
要设计一种数据结构,可以记录26个字母出现的次数。
好的方法就是把26个变量放入一个数组,然后使用字符代码选择数组中合适的元素。
每个元素里存放一个整型数值,表示对应于数组中该下标的字母当前出现的次数。
数组letterCounts
,声明如下:
int letterCounts[NLetters];
Nletters
被定义为常数26。
在输入数据中每出现一个字母时,就要增加letteCounts
中相应元素的值。
寻找需要自增的元素的过程,就是用字符运算把一个字母转换成在范围0~25中的整数。
这个转换过程由函数LetterIndex
实现:
int LetterIndex(char ch) {
if (isalpha(ch)) {
return (toupper(ch) - 'A');
} else {
return (-1);
}
}
其中,character相关返回值,示例如下:
// character vs ASCII value
printf("%d\n", 'a');
printf("%c\n", 'a');
printf("%d\n", toupper('a'));
printf("%c\n", toupper('a'));
97
a
65
A
为了记录每个字母出现的次数,需要:
(1) 通过调用函数LetterIndex
把字母转化为下标值。
(2) 如果下标值不是-1,就要自增数组letterCounts
中相应下标值的元素的值。
函数RecordLetter
的实现如下:
void RecordLetter(char ch, int letterCounts[]) {
int idx;
idx = LetterIndex(ch);
if (idx != -1) {
letterCounts[idx]++;
}
}
其中,检测函数LetterIndex
返回的值是否是-1很重要。
如果不作这个检测,程序就有可能增加 lettercount[-1]
中的数值。
即使此元素事实上不存在,大部分的编译器还是会检查正好位于数组开始前的那个整型字。
因为在数组letterCounts
之前的内存中可能存放程序需要的其他数据,所以改变这个位置的数据就有可能导致程序运行结果不正确,也有可能使程序无法运行。
在记录每个字母出现的次数之前,应该先确保数组中元素的初始值在程序运行之前为0。
函数ClearIntegerArray
,实现如下:
void ClearIntegerArray(int array[], int n){
int i;
for (i = 0; i < n; i++)
{
array[i] = 0;
}
}
最后,函数DisplayLetterCounts
生成字母出现频率表,实现如下:
void DisplayletterCounts(int letterCounts[]) {
char ch;
int num;
for (ch = 'A'; ch <= 'Z' ; ch++) {
num = letterCounts[LetterIndex(ch)];
if (num != 0) {
printf("%c %d\n", ch, num);
}
}
}
其中,for循环语句的循环范围是从’A’~’Z’,而不是从0~25是很重要的。
因为用对应于问题的下标值,会有利于理解for循环语句。
最后,完整的实现如下:
/*
* File: countlet.c
* ----------------
* This program counts the occurrences of individual letters
* that appear in the text inputted by user.
*/
#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>
#include "strlib.h"
#define NLetters 26
/* Function Prototype */
static void ClearIntegerArray(int array[], int n);
static void CountLetters(int letterCounts[]);
static void CountLettersInString(string str, int letterCounts[]);
static void RecordLetter(char ch, int letterCounts[]);
static int LetterIndex(char ch);
static void DisplayletterCounts(int letterCounts[]);
/* Main Program */
main() {
int letterCounts[NLetters];
printf("This program counts letter frequencies.\n");
printf("Enter a blank line to signal end of input.\n");
ClearIntegerArray(letterCounts, NLetters);
CountLetters(letterCounts);
DisplayletterCounts(letterCounts);
}
/* Function */
static void ClearIntegerArray(int array[], int n){
int i;
for (i = 0; i < n; i++)
{
array[i] = 0;
}
}
static void CountLetters(int letterCounts[]) {
char line[1000];
int i, length;
string str;
while (true)
{
str = "";
fgets(line, 1000, stdin);
if (IthChar(line, 0) == '\n') {
break;
} else {
length = StringLength(line);
for (i = 0; i < length; i++) {
str = Concat(str, CharToString(IthChar(line, i)));
}
}
CountLettersInString(str, letterCounts);
}
}
static void CountLettersInString(string str, int letterCounts[]) {
int i, length;
length = StringLength(str);
for (i = 0; i < length;i++) {
RecordLetter(IthChar(str, i), letterCounts);
}
}
static void RecordLetter(char ch, int letterCounts[]) {
int idx;
idx = LetterIndex(ch);
if (idx != -1) {
letterCounts[idx]++;
}
}
static int LetterIndex(char ch) {
if (isalpha(ch)) {
return (toupper(ch) - 'A');
} else {
return (-1);
}
}
static void DisplayletterCounts(int letterCounts[]) {
char ch;
int num;
for (ch = 'A'; ch <= 'Z' ; ch++) {
num = letterCounts[LetterIndex(ch)];
if (num != 0) {
printf("%c %d\n", ch, num);
}
}
}
参考
《C语言的科学和艺术》 —— 第11章 数组