打印输入单词字符数量统计直方图-C语言第二版

1. 编程要求

多年前写过一篇用 C 语言实现打印单词字符数量统计的直方图的文章, 现在看上去有些混乱, 对一些任务划分不清晰, 全部混在一起. 于是重写了这个编程题, 希望可以给初学者一些参考, 并且我分别用 C, C++, Java, Python 四种语言完成了这道编程题, 有兴趣的可以看我另外三篇文章用 C++ / Java / Python 实现的. 当然, 没有标准答案, 只能是参考. 原题是<<C程序设计语言第2版>>练习1-13的编程题. 原文如下:

练习1-13  编写一个程序, 打印输入中单词长度的直方图. 水平方向的直方图比较容易绘制, 垂直方向的直方图则要困难些.

我这里实现的是 垂直 方向的, 对于初学者来说学习语言只要时间和精力允许, 则尽可能的挑战难一点的要求去完成. 如果你还能用 3 种及以上的方式(指的是同一种语言)完成编程要求, 那更好

我发现如果一篇文章一上来就扔一堆代码, 文字讲解太少的话, 没什么人愿意有耐心的看, 尤其是关键部分的算法讲解太少的话. 所以我这里还是对这个小小编程题的核心算法部分描述一下. 其它三篇 C++ / Java / Python 的核心部分都是使用的相同的算法. 所以我这里核心的那一点算法描述都是一样的, 只是用不同语言去实现而已. OK, 核心部分并且也是实现垂直直方图的难点的部分的算法如下:

2. 算法设计

直方图是竖着显示单词的数量,而不是横着显示,可程序只能一行一行打印,这里是打印垂直直方图的一个难点.那我们怎么确定什么时候该打印图案,什么时候不打印图案呢?

关键算法

我们定义一个 words 数组下标代标表示单词长度, 数组值表示对应下标长度值的单词数量.可以发现直方图Y轴部分刚好和行号相对应,在我们统计的单词数组中的单词数量刚好可以和行号对应,比如长度为 2 的单词我们统计有 5 个,那我们打印Y轴最少要打印 5 行,同时从第 5 行并与之对应的单词长度的列上就要开始打印图案.

如何实现

我们通过一个双层循环,外层控行最大行数,内层用迭代变量例如 j 来访问单词数组 words[] .比如在第 1 列就是 words[j] 上与当前行号例如第 6 行比对,如果 words[j] >= 6 为真则说明j长度的单词还有 6 个没打印,则从当前第 6 行就要开始打印图案. 这样依次打印第 6 行所有长度(列) j = 6 ~ 1,然后下一轮进入第 5 行打印有 5 个单词数量(只要单词数量pw[j] >= i行号, 打印过第6行的那一列在5, 4, 3, 2, 1行继续打印)的相应单词长度的数量,一直到长度为1. (注: 下面用指针 pw 访问words数组)

所以, 关键算法部分转换成代码就这一点点:


    /*打印Y轴和直方图案*/
    for (i = len - 1; i > 0; i--) {      /*i控制直方图高度即行数*/
        printf("%3d|", i);               /*打印Y轴量度值和Y轴*/
        for (j = 1; j < len; j++) {     /*j作下标来访问words数组中单词数量*/

            if (pw[j] >= i) {               /*如果当前长度单词字符数不少于行号这么多数量*/
                printf("%3c", '*');      /*则打印直方图图案*/
            } else {                          /*否则当前长度单词数打印空白*/
                printf("%3c", ' ');
            }

 

3. 完整代码

/* << C程序设计语言第二版 >> 练习1-13 实现打印输入中单词长度的直方图*/
/* 本程序实现的是垂直方向的直方图 */
#include <stdio.h>
#include <ctype.h>
#define MAXLEN 10

/*初始化单词数量数组*/
void InitWords(int (*pw)[MAXLEN]);
/*完成各单词长度的统计*/
void CountWord(int *pw, int len);
/*根据words数组信息打印直方图*/
void PrintHisto(int *pw, int len);

int main()
{
	int words[MAXLEN];          /*存放各长度的单词数量,下标即单词长度*/

	InitWords(&words);          /*初始始化单词数量数组*/
	CountWord(words, MAXLEN);   /*统计输入的各长度单词数量*/
	PrintHisto(words, MAXLEN);  /*根据words数组信息打印直方图*/

	printf("\nDone.\n\n");
	return 0;
}

/*初始化单词数量数组*/
/*pw是一个数组指针,这里传给pw的是数组words的地址*/
/*是传址,不是常见的传值,当然无论传值还是传址都可以*/
/*这个数组指针有点像函数指针,只不过右边是方括号*/
void InitWords(int (*pw)[MAXLEN])
{
	int i;
	for (i = 0; i < MAXLEN; i++) {
		(*pw)[i] = 0; /*pw是指向数组的指针,*pw代表数组*/
	}
}

/*完成各单词长度的统计*/
void CountWord(int *pw, int len)
{
	char ch;
	int wordin = 0;  /*是否在单词中的状态*/
	int chcount = 0; /*统计单词的长度*/

	printf("\nEnter some words(# to quit): \n");
	while ((ch = getchar()) != '#') {
		if (isalpha(ch)) {                         /*如果是字母*/
			if (0 == wordin) { wordin = 1; }       /*不在单词内则置为进入单词*/
			if (chcount < len - 1) { chcount++; }  /*如果当前单词不超长则计数*/
		} else {                                   /*如果是非字母字符*/
			if (1 == wordin) {                     /*如果在单词中则*/
				wordin = 0;                        /*置为出单词状态*/
				pw[chcount]++;                     /*当前长度单词数加1*/
				chcount = 0;                       /*重置单词字符个数*/
			}
		}
	}
	pw[chcount]++; /*统计最后一个单词*/
}


/*根据words数组信息打印直方图*/
/*我们将打印直方图分成两大部分*/
/*第一部分是打印Y轴和直方图图案,第二部分是打印X轴及下方*/
void PrintHisto(int *pw, int len)
{
	int i, j;

	/*第一部分,打印Y轴及直方图图案*/
	printf("\n   Y The count of word\n");
	printf("%4c\n", '|');
	/*打印Y轴和直方图案*/
	for (i = len - 1; i > 0; i--) {  /*i控制直方图高度即行数*/
		printf("%3d|", i);           /*打印Y轴量度值和Y轴*/
		for (j = 1; j < len; j++) {  /*j作下标来访问words数组中单词数量*/
			if (pw[j] >= i) {        /*如果当前长度单词字符数不少于行号这么多数量*/
				printf("%3c", '*');  /*则打印直方图图案*/
			} else {                 /*否则当前长度单词数打印空白*/
				printf("%3c", ' ');
			}
		}
		printf("\n");
	}

	/*第二部分,打印X轴及量度值*/
	printf("  +");
	for (i = 0; i < len; i++) {         /*X轴*/
		printf("---");
	}
	printf(">X The length of word\n");
	printf("  0 ");                     /*打印原点*/
	for (i = 1; i < len; i++) {         /*X轴数字*/
		printf("%3d", i);
	}
	printf("\n");
}

4. 运行结果

5. 结语

写文章不容易, 支持原创, 如转载请注明出处. 喜欢就点赞收藏, 您的点赞收藏是我创作的动力. 祝我的文章读者工作顺利. 谢谢.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值