习题3-1 分数统计(stat)
输入一些学生的分数,哪个分数出现的次数最多?如果有多个并列,从小到大输出。
任务1:分数均不超过100的非负整数
任务2:分数均不超过100的非负实数,但最多保留两位小数。
这个类似单词统计词频,按字典序输出频率最高的那些。
【思路】
pic
任务1和任务2差不多,换成double类型即可;现在以任务1为例解答——
一种思路是从第二个读入的数字开始,从已有的集合(数组)中寻找,若重复则该元素的频率变量自增;否则添加新元素;
最后根据频率变量排序,然后截取频率最高的那段(多个或者1个),再对该段按分数变量排序,最后输出这段数组的分数;
#include <stdio.h> #include <stdlib.h> #define N 10000 typedef struct node { int val; int freq; }node; node a[N];//集合 int len = 0;//集合大小 int isInclude(int key) { int i; for (i = 0; i != len; i++) { if (key == a[i].val) { a[i].freq++; return 1; } } return 0; } int cmp1(const void* a, const void *b) { return (*(node *)b).freq - (*(node *)a).freq;//descending order } int cmp2(const void* a, const void *b) { return (*(node *)a).val - (*(node *)b).val; } int cmp1(const void* a, const void *b); int cmp2(const void* a, const void *b); int isInclude(int key); int main(void) { int n, tmp; int i, j; scanf("%d", &n); for (i = 0; i != n; i++) { a[i].val = -1; a[i].freq = 1; } extern int len; scanf("%d", &(a[len++].val)); for (i = 0; i != n-1; i++) { scanf("%d", &tmp); if (!isInclude(tmp)) { a[len++].val = tmp; } //else the element's freq++ @isInclude(); } qsort(a, len, sizeof (node), cmp1); for (i = 0; i != len; i++) { if (a[i+1].freq != a[i].freq) { tmp = i; break; } } qsort(a, tmp+1, sizeof (node), cmp2); for (i = 0; i != tmp+1; i++) printf("%d\n", a[i].val); return 0; }
习题3-2 单词的长度(word)
输入若干个单词,输出它们的平均长度。单词只包含大写字母和小写字母,用一个或多个空格隔开。
#include <stdio.h> #include <string.h> #define N 10000 char str[N]; char buf[N/100]; int len1(char *s); int len2(char *s); int len1(char *s) { int count = 0; int words = 0; char *p = s; for (; *p != '\n'; p++) { if (*p != ' ') ++count; if (*p != ' ' && (*(p+1) == ' ' || *(p+1) == '\n')) ++words; } return count/words; } int len2(char *s) { int words = 0; int count = 0; char *p = s; while ( 1 == sscanf(p, "%s", buf) ) { p += strlen(buf)+1; count += strlen(buf); ++words; } return count/words; } int main(void) { fgets(str, sizeof (str), stdin); printf("%d\n", len1(str));//or you can call len2(str); return 0; }对这个题目做了一个假定。约定输入的总长度不超过10000个字符,每个单词长度不超过100个字符。
(其实也可以只利用一个getchar()写一个就地的算法(不需要额外辅助空间),也就不需要这个假设了)
这样求单词平均长度的方法可以有两个,分别写成函数len1()和len2()
习题3-3 乘积的末3位
输入若干个整数(可以是正数、负数或者零),输出它们的乘积的末3位。这些整数中会混入一些由大写字母组成的字符串,你的程序应该忽略它们。提示:试试看,在执行scanf("%d")时输入一个字符串会怎样?
输入:AB123CC BB123123321321 DDD22 888888888888888888888888888ZZ -411B
输出:968
输入:AA-11BBB D2CCC
输出:-22
假定末3位是指,不足3位就输出数字本身,如果是负数则包括负号,比如结果是-12,则输出-12;结果是11,则输出11,结果是0,则输出0;
分析:
必须假定整数(包括字母)长度小于某个数,下面假定长度不超过1000,整数个数不超过100;
设计算法如下:
每读入一个字符串,保存数字部分的后三位(位数不足则保存数字本身),同时判断符号,是负数则标志变量flag(初始为1)改变符号;
初始product为1,按先后逐个处理数字串, 每次处理过程中,product与当前数字串的数字相乘后再模1000,更新到product;
如果product<100,则输出flag*product,否则直接输出product;
为了确保乘积不会溢出,所以必须每步相乘后都要取模。
#include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #define N 100000 #define M 5000 #define L 5 char input[N]; char str[M]; char tmp[L]; char rev[L]; int main(void) { int i, len, k; int flag = 1; int product = 1; char *p = NULL; fgets(input, sizeof (input), stdin); p = input; while (sscanf(p, "%s", str) == 1) { len = k = 0; for (i = strlen(str)-1; i != -1; i--) { if (len != 3 && 1 == isdigit(str[i])) tmp[len++] = str[i]; if (str[i] == '-') { flag = -flag; break; } } tmp[len] = '\0'; while (len > 0) rev[k++] = tmp[--len]; rev[k] = '\0'; product *= atoi(rev); product %= 1000; p += strlen(str)+1; while (*p == ' ') p++;/* 滤空 */ } printf("%d\n", product < 100 ? product*flag : product); return 0; }
习题3-4
编程程序读入一行恰好包括一个+或-或*的表达式,输出它的值。运算符保证是二元运算符,且两个运算数均不超过100的非负整数。运算数和运算符可以紧挨也可以有一个或多个空格、TAB隔开。行首尾均可以有空格。提示:选择合适的输入方法可以将问题简化。
#include <stdio.h> #define N 1000 char str[N]; int main(void) { char *p = NULL; int op1, op2; fgets(str, sizeof(str), stdin);//it will contains '\n'; for (p = str; *p != '\n'; p++) { if (*p == '+' || *p == '-' || *p == '*') break; } if (*p != '\n'){ switch(*p) { case '+': sscanf(str, "%d + %d", &op1, &op2); printf("%d\n", op1+op2); break; case '-': sscanf(str, "%d - %d", &op1, &op2); printf("%d\n", op1-op2); break; case '*': sscanf(str, "%d * %d", &op1, &op2); printf("%d\n", op1*op2); break; } } return 0; }
习题3-5 旋转
输入一个n*n字符矩阵,把它左旋90度后输出
#include <stdio.h> #define N 100 char a[N][N]; int main(void) { int n; int i, j; scanf("%d", &n); for (i = 0; i != n; i++) { for (j = 0; j != n; j++) scanf("%s", &a[i][j]); } for (j = n-1; j != -1; j--) { for (i = 0; i != n; i++) printf("%c ", a[i][j]); printf("\n"); } return 0; }
注意:a[i][j]
i表示的是列
j表示的是行
比如输出
1 2 3
4 5 6
7 8 9
的第三列,应该是
for (int i = 0; i != n; i++)
printf("%d ", a[i][2]);
习题3-6 进制转换1
输出基数b( 2 <= b <= 10)和正整数n(十进制),输出n的b进制表示
思路:关键是除留余数法时,结束的条件可以是当前剩下的数字<=基数或者当前剩下的数字的==它的模
至于逆序输出,可以考虑用递归实现
递归版本:
#include <stdio.h> void trans(int n, int b) { if (n >= b) trans(n/b, b); printf("%d", n%b); } void trans(int n, int b); int main(void) { int b, n; scanf("%d %d", &b, &n); trans(n, b); printf("\n"); return 0; }
非递归版本:
#include <stdio.h> #define N 100 int a[N]; int main(void) { int b, n; int k = 0; int i; scanf("%d %d", &b, &n); while (n >= b) { a[k++] = n%b; n /= b; } a[k++] = n%b; for (i = k-1; i != -1; i--) printf("%d", a[i]); printf("\n"); return 0; }
习题3-7 进制转换2
输出基数b( 2 <= b <= 10)和正整数n(b进制),输出n的十进制表示
思路,比如八进制的678,换成十进制就是8*8的0次方+7*8的1次方+6*8的2次方,因此很容易编写
#include <stdio.h> #include <string.h> #define N 100 char str[N]; int main(void) { int n, i, k, tmp; int res = 0; scanf("%d %s", &n, str); for (i = strlen(str)-1; i != -1; i--) { tmp = str[i]-'0'; k = strlen(str)-1-i; while(k--) tmp *= n; res += tmp; } printf("%d\n", res); return 0; }
习题3-8 手机键盘
略