C程序设计语言(部分参考答案)
观前提醒:该参考答案并不完全,只是我写到了哪些习题,就将答案记录了下来,我保证这些代码在我的电脑上是可以跑通的,但是这些答案有时并不是最优解。
第二章
P37 T 2-3
#include<stdio.h>
int htoi(const char arrh[]);
int main()
{
char arrh[100];
char c;
a: printf("请输入一个16进制的数字(支持0x前缀):");
/*for (int i = 0; i < 99 && (c = getchar()) != '\n'; i++)
arrh[i] = c;*/
scanf("%s", arrh);
long index = htoi(arrh);
if (index == -1)
{
printf("非法数字,请重新输入\n");
goto a;
}
printf("十进制为:%ld\n", index);
return 0;
}
int htoi(const char arrh[])
{
int i = 0;
int result = 0;
// 判断字符数组的前两位是否是0x或0X
if (arrh[i] == '0' && (arrh[i + 1] == 'x' || arrh[i + 1] == 'X'))
i = 2;
// 依次遍历转换数组元素
while (arrh[i] != '\0')
{
int val;
char c = arrh[i];
// 有两种情况
// 如果是数字
if (c >= '0' && c <= '9')
val = c - '0';
// 如果是字母(分大小写)
else if (c >= 'A' && c <= 'F')
val = 10 + (c - 'A');
else if (c >= 'a' && c <= 'f')
val = 10 + (c - 'a');
else
return -1;
// 关键点 把每次遍历转换后的值认为是最后一位,如果后面又遍历到了数字就把前面得到的值(总和)前进一位(值 * 16)
result = result * 16 + val;
i++;
}
return result;
}
P38 T 2-4
#include<stdio.h>
void squeeze(char s1[], char s2[]);
int main()
{
char s1[100];
char s2[100];
printf("请输入s1字符串:");
scanf("%s", s1);
printf("请输入s2字符串:");
scanf("%s", s2);
squeeze(s1, s2);
printf("修改后的s1字符串为:%s\n", s1);
return 0;
}
void squeeze(char s1[], char s2[])
{
int i, j, k;
// 遍历s2数组
for (i = 0; s2[i] != '\0'; i++)
{
// 遍历s1数组,检查是否有与s2中元素匹配的情况
for (j = 0, k = 0; s1[j] != '\0'; j++)
{
// 如果没有,就把该s1[j]放在s1[k]处
if (s1[j] != s2[i])
s1[k++] = s1[j];
}
s1[k] = '\0';
}
}
- Bug:
- 输入空格将导致程序出错
- 处理汉字有时会出现奇奇怪怪的问题
P38 T 2-5
#include<stdio.h>
int any(char s1[], char s2[], char position[]);
int main()
{
char s1[100];
char s2[100];
char result[100];
printf("输入s1:");
scanf("%s", s1);
// printf("输入的s1:%s\n", s1); // 测试节点
printf("输入s2:");
scanf("%s", s2);
int index = any(s1, s2, result);
printf("index的值:%d\n", index); // 测试节点
if (index == -1)
{
printf("s2中字符均没有出现在s1中,代码:%d\n", index);
}
else
{
// printf("我执行了吗?\n"); // 测试节点
for (int i = 0; i < index; i++) // 这里需要注意,result是int数组,不能用'\0'判断结束,需要记录实际存储的位置数量
{
printf("位置信息为:%d\n", result[i]);
}
}
// printf("位置信息0为:%d\n", result[1]); // 测试
return 0;
}
int any(char s1[], char s2[], char position[])
{
// 1.对s2进行去重并按照原数组字母出现顺序排序
int news2_len = 0;
char news2[100]; // 存储去重后的数组
for (int i = 0; s2[i] != '\0'; i++)
{
// 定义一个布尔量
int is_exit = 0; // 0表示新数组中无原数组元素
for (int j = 0; j < news2_len; j++)
{
if (news2[j] == s2[i])
{
// 修改布尔值
is_exit = 1; // 1表示新数组中有原数组元素
}
}
if (is_exit == 0) // 新数组中无原数组元素,存储该元素,并将新数组索引前进一位用于存储新元素
{
news2[news2_len++] = s2[i];
// printf("news2_len现在为:%d\n", news2_len); // 测试节点
}
}
news2[news2_len] = '\0';
// printf("去重后的s2数组:%s\n", news2); // 测试节点
// 2.检查s2元素在s1数组中第一次出现的位置
int pos = 0;
// 定义一个布尔值
int is_com = 0; // 0表示s2中所有元素均未在s1数组中出现
// 遍历去重后的s2数组
for (int i = 0; news2[i] != '\0'; i++)
{
// 遍历s1数组
for (int j = 0; s1[j] != '\0'; j++)
{
// 判断s2中数组是否出现在s1数组中
if (s1[j] == news2[i])
{
// 修改布尔值
is_com = 1; // 1表示s2中有元素出现在s1数组中
// 如果出现就记录位置
position[pos++] = j;
// printf("现在的位置信息为:%d\n", position[pos - 1]); // 测试
// (我们只要第一次出现的位置信息)该循环任务完成,结束内循环
break;
}
}
position[pos] = '\0';
}
if (is_com == 0)
return -1;
return pos;
}
P40 T 2-6
#include<stdio.h>
unsigned int setbits(unsigned int x, int p, int n, unsigned int y);
int main()
{
unsigned int x, y;
int p, n;
x = 107, y = 204;
p = 5, n = 3;
unsigned int rs = setbits(x, p, n, y);
printf("结果:%u (二进制:", rs);
// 辅助打印二进制(便于验证)
for (int i = 7; i >= 0; i--) {
printf("%d", (rs >> i) & 1);
}
printf(")\n");
return 0;
}
// 将x中从p位开始的n个(二进制)位设置为y中最右侧n位的值,x的其余各位保持不变
unsigned int setbits(unsigned int x, int p , int n, unsigned int y)
{
// 1.清空x中从p位开始的n个位
// 定义一个n位1的掩码
unsigned int mask_n_1 = (1 << n) - 1;
// 把n位1移动到x要清除的位上
unsigned int mask_clear = mask_n_1 << (p - n + 1);
// 清除:对0与运算
// 保留:对1与运算
unsigned int x_rs = x & ~mask_clear;
// 2.获取y中最右侧n位的值
// 保留:对1与运算
unsigned int y_rs1 = y & mask_n_1;
// 将取到的值移动到x清除的位上
unsigned int y_rs2 = y_rs1 << (p - n + 1);
// 3.合并x与y,并返回修改后的x
// 合并:常用对0或运算
return x_rs | y_rs2;
}
P40 T 2-7
#include<stdio.h>
unsigned int invert(unsigned int x, int p, int n);
int main()
{
int n, p;
n = 3;
p = 5;
unsigned int x = 240;
unsigned int ob_x = invert(x, p, n);
printf("指定位取反后的结果为:%u\n", ob_x);
for (int i = 7; i >= 0; i--)
{
printf("%d", (x >> i) & 1);
if (i % 4 == 0)
printf(" ");
}
printf("\n");
for (int i = 7; i >= 0; i--)
{
printf("%d", (ob_x >> i) & 1);
if (i % 4 == 0)
printf(" ");
}
return 0;
}
// x中从第p位开始的n个(二进制)位求反,x的其余各位保持不变
unsigned int invert(unsigned int x, int p, int n)
{
// 定义一个n位1的掩码
unsigned int mask_n_1 = (1 << n) - 1;
// 将掩码移位到x的p位
unsigned int mask_shift = mask_n_1 << (p - n + 1);
// 取反:与1按位异或运算取反,与0按位异或运算保留
unsigned int ob_x = x ^ mask_shift;
return ob_x;
}
P40 T 2-8
#include<stdio.h>
unsigned int rightrot(unsigned int x, int n);
int main()
{
int n = 3;
unsigned int x = 170;
printf("%u二进制表示为:", x);
for (int i = 7; i >= 0; i--)
{
printf("%d", (x >> i) & 1);
if (i % 4 == 0) printf(" ");
}
printf("\n");
unsigned int index = rightrot(x, n);
printf("修改后的数字为:%u(二进制表示为:", index);
for (int i = 7; i >= 0; i--)
{
printf("%d", (index >> i) & 1);
if (i % 4 == 0) printf(" ");
}
printf(")\n");
return 0;
}
unsigned int rightrot(unsigned int x, int n)
{
// 1.得到x的最高位的索引
int index = 0;
unsigned int y = x;
// 对x一直右移,直到x<1,说明x最高位移动到最低位
if (x == 0) return 0;
while (y > 1)
{
y >>= 1;
index++;
}
// 如果循环次数大于有效位数的话,我们领循环次数等于有效次数,反应的结果是return原数
if (n > index + 1) n = index + 1;
// 2.得到x的最右侧n位数
// 设置n位1掩码
unsigned int mask_n_1 = (1 << n) - 1;
// 与1与运算 得到x最右侧n位数
unsigned int right_n_x = x & mask_n_1;
// 3.将x最右侧n位数置0,并右移n位
unsigned int remove_right_x = (x & ~mask_n_1) >> n;
// 4.将right_n_x左移 index-n+1 位,并与remove_right_x合并
unsigned int result_x = (right_n_x << (index - n + 1)) | remove_right_x;
return result_x;
}
P40 T 2-9
#include<stdio.h>
int main()
{
int n = 64;
printf("该数二进制表示为:");
for (int i = 7; i >= 0; i--)
{
printf("%d", (n >> i) & 1);
if (i % 4 == 0) printf(" ");
}
printf("\n");
int count = bitcount(n);
printf("该数的二进制中有几个1:%d\n", count);
int rs = chest(n);
printf("该数是否是2的幂:%d\n", rs);
return 0;
}
// 统计一个数转换成二进制后,位值为1的个数
int bitcount(int x)
{
int count = 0;
while (x != 0)
{
x &= (x - 1);
count++;
}
return count;
}
// 检查x是否是2的幂(1T 0F)
int chest(int x)
{
if ((x &= (x - 1)) == 0) return 1;
else return 0;
}
P42 T 2-10
#include<stdio.h>
int lower(char c);
int main()
{
char c = lower('y');
printf("%c\n", c);
return 0;
}
// 大写字母转换成小写字母(如果传入的是小写字母,则return原字符)
int lower(char ch)
{
return ch >= 'A' && ch <= 'Z' ? ch += 32 : ch;
}
第三章
P57 T 3-1
#include<stdio.h>
int main()
{
int x = 5;
int v[4 * 10] = { 0,1,2,3,4,5,6,7,8,9 };
int index = binsearch(x, v, 10);
if (index == -1)
printf(" x 不在数组中!\n");
else
printf(" x 在数组的 %d 位置\n", index);
return 0;
}
// 二分查找,找到数组v中x元素的所在位置,并返回,数组v必须是升序排列
int binsearch(int x, int v[], int len)
{
// 1. 定义变量:low=查找范围左边界,high=右边界,mid=中间位置
int low, high, mid;
// 2. 初始化边界:low从数组第一个元素(索引0)开始,high到最后一个元素(索引len-1)
low = 0;
high = len - 1;
// 3. 循环缩范围:只要左边界 < 右边界,就继续折半(直到low==high)
while (low < high)
{
// 4. 计算中间位置:整数除法向下取整(比如(0+4)/2=2,(3+4)/2=3)
mid = (low + high) / 2;
// 5. 核心判断:利用数组升序特性缩范围
if (x <= v[mid])
// x <= 中间值 → x在左半区(包括mid),把右边界拉到mid
high = mid;
else
// x > 中间值 → x在右半区(mid右侧),把左边界推到mid+1(mid已排除)
low = mid + 1;
}
// 6. 循环结束后:low==high,验证该位置是否是目标值
return (v[low] == x) ? low : -1;
}
P59 T 3-2
#include<stdio.h>
char* escape(char s[], char t[]);
char* unescape(char s[], char t[]);
int main()
{
char s[1000], t[1000];
int i, c;
for (i = 0; i < 999; i++)
{
if ((c = getchar()) == EOF)
break;
t[i] = c;
}
t[i] = '\0';
escape(s, t);
printf("转换后的字符串为:\n");
for (int j = 0; j < 999 && s[j] != '\0'; j++)
printf("%c", s[j]);
printf("\n");
unescape(s, t);
printf("再次还原后的字符串为:\n");
for (int k = 0; k < 999 && s[k] != '\0'; k++)
printf("%c", s[k]);
printf("\n");
return 0;
}
// 将字符串t复制到字符串s中,并在复制过程中将换行符、制表符等不可见字符分别转换为\n\t等相应的可见的转义字符序列,用switch结构
char* escape(char s[], char t[])
{
int j = 0;
for (int i = 0; t[i] != '\0'; i++)
{
int c;
c = t[i];
switch (c){
case '\n': // 换行符→转义为\n
s[j++] = '\\'; // 先存反斜杠
s[j++] = 'n'; // 再存n
break;
case '\t': // 制表符→转义为\t
s[j++] = '\\';
s[j++] = 't';
break;
case ' ': // 空格→替换为~(按你的需求)
s[j++] = '\\';
s[j++] = '~';
break;
default: // 普通字符直接复制
s[j++] = c;
break;
}
}
s[j] = '\0';
return s;
}
// 还原
char* unescape(char s[], char t[])
{
int i, j = 0;
for (i = 0; t[i] != '\0'; i++)
{
int c;
c = t[i];
if (c == '\\')
{
c = t[i++];
switch (c)
{
case 'n':
s[j++] = '\n';
break;
case 't':
s[j++] = '\t';
break;
case '~':
s[j++] = ' ';
break;
default:
s[j++] = '\\';
break;
}
}
else
s[j++] = c;
}
s[j] = '\0';
return s;
}
P61 3.5
#include<stdio.h>
void shellsort(int v[], int len);
int main()
{
int v[11 * 4] = { 24,26,17,7,345,88,2,6,89,2,6 };
shellsort(v, 11);
for (int i = 0; i < 11; i++)
{
printf("%d ", v[i]);
}
return 0;
}
// 希尔排序
void shellsort(int v[], int len)
{
int gap, i, j, temp;
for ( gap = len / 2; gap >= 1 ; gap /= 2)
{
for ( i = gap; i < len; i++)
{
for ( j = i - gap; j >= 0 && v[j] > v[j + gap]; j -= gap)
{
temp = v[j];
v[j] = v[j + gap];
v[j + gap] = temp;
}
}
}
}
P64 T 3-4
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
void itoa(int n, char a[]);
int main()
{
int n;
char a[1000];
scanf("%d", &n);
itoa(n, a);
printf("%s", a);
return 0;
}
// 改进版itoa函数
void itoa(int n, char a[])
{
int sign = 0;
unsigned int un;
if (n < 0)
{
sign = 1;
un = (unsigned)(-n);
}
else
un = (unsigned)n;
int i = 0;
// 将数字转换成字符(倒序)
do
{
a[i++] = un % 10 + '0';
} while ((un /= 10) > 0);
if (sign)
a[i++] = '-';
a[i] = '\0';
// 反转字符
int len = strlen(a);
for (int j = 0; j < len / 2 ; j++)
{
unsigned int temp = a[j];
a[j] = a[len - 1 - j];
a[len - 1 - j] = temp;
}
}
P64 T 3-5
#include<stdio.h>
#include<string.h>
void itob(int n, char s[], int b);
void reverse(char s[]);
int main()
{
int n = 703710;
char s[1000];
int b = 16;
itob(n, s, b);
printf("%s", s);
return 0;
}
// 反转字符
void reverse(char s[])
{
int len = strlen(s);
for (int i = 0; i < len / 2; i++)
{
char c = s[i];
s[i] = s[len - 1 - i];
s[len - 1 - i] = c;
}
}
// 将n转换为以b为底的字符串存储在s中
void itob(int n, char s[], int b)
{
int sign = 0;
unsigned int num;
if (n < 0)
{
sign = 1;
num = (unsigned)(-n);
}
else
num = (unsigned)n;
int i = 0;
// 特殊情况
if (num == 0)
{
s[i++] = '0';
s[i] = '\0';
return 0;
}
// 转进制
while (num != 0)
{
int re = num % b;
if (re < 10)
s[i++] = re + '0';
else
s[i++] = 'A' + (re - 10);
num /= b;
}
if (sign)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}
P64 T 3-6
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
void itoa(int n, char a[], int width);
int main()
{
int n;
char a[1000];
scanf("%d", &n);
itoa(n, a, 100);
printf("%s", a);
return 0;
}
// 改进版itoa函数
void itoa(int n, char a[], int width)
{
int sign = 0;
unsigned int un;
if (n < 0)
{
sign = 1;
un = (unsigned)(-n);
}
else
un = (unsigned)n;
int i = 0;
// 将数字转换成字符(倒序)
do
{
a[i++] = un % 10 + '0';
} while ((un /= 10) > 0);
if (sign)
a[i++] = '-';
a[i] = '\0';
// 反转字符
int len = strlen(a);
for (int j = 0; j < len / 2; j++)
{
char temp = a[j];
a[j] = a[len - 1 - j];
a[len - 1 - j] = temp;
}
// 判断是否要添加空格
if (len < width)
{
// 先将元素移动到最右端
for (int k = len; k >= 0; k--)
a[k + (width - len)] = a[k];
// 左边添加空格
for (int l = 0; l < width - len; l++)
a[l] = ' ';
// 避免极端情况(width == len)
a[width] = '\0';
}
}
第四章
P70 T 4-1
#include<stdio.h>
#define MAXLINE 1000
int getline(char s[]);
int strrindex(char s[], char t[]);
int streindex(char s[], char t[]);
char pattern[] = "ould";
int main()
{
char s[MAXLINE];
int rindex = 0;
int eindex = 0;
if (getline(s) > 0)
rindex = strrindex(s, pattern);
eindex = streindex(s, pattern);
if (rindex == -1)
printf("无,错误代码:%d\n", rindex);
else
printf("t在s中出现的最右侧起始位置:%d\n", rindex);
if (eindex == -1)
printf("无,错误代码:%d", eindex);
else
printf("t在s中出现的最右侧结尾位置:%d", eindex);
return 0;
}
// 保存一个字符串并返回字符串的长度
int getline(char s[])
{
int i;
char c;
for ( i = 0; (c = getchar()) != EOF && c != '\n'; i++)
{
s[i] = c;
}
s[i] = '\0';
return i;
}
// 返回字符串t在s中最右边的起始位置,没有返回-1
int strrindex(char s[], char t[])
{
int pos = -1;
int i, j, k;
// 遍历s字符串
for (i = 0; s[i] != '\0'; i++)
{
for (j = i, k = 0; t[k] != '\0' && t[k] == s[j]; j++, k++)
;
// 匹配完全的必要条件
if (k > 0 && t[k] == '\0')
pos = i;
}
return pos;
}
// 返回字符串t在s中最右边的结尾位置,没有返回-1
int streindex(char s[], char t[])
{
int pos = -1;
int i, j, k;
// 遍历s字符串
for (i = 0; s[i] != '\0'; i++)
{
for (j = i, k = 0; t[k] != '\0' && t[k] == s[j]; j++, k++)
;
// 匹配完全的必要条件
if (k > 0 && t[k] == '\0')
pos = i + k - 1;
}
return pos;
}
P70 4.2
#include<stdio.h>
#include<ctype.h>
double atof(char s[]);
int main()
{
char s[100] = "123.45";
double n = atof(s);
printf("%lf", n);
return 0;
}
// 将字符串浮点数转换为数值浮点数
double atof(char s[])
{
int i;
int sign = 1;
double val = 0.0;
// 空格跳过
for (i = 0; s[i] == ' '; i++)
;
// 判断符号
if (s[i] == '+' || s[i] == '-')
{
sign = (s[i] == '+' ? 1 : -1);
i++; // 跳过符号
}
// 处理数据(小数点前的数据)
while (isdigit(s[i]))
{
val = val * 10 + (s[i] - '0');
i++;
}
// 判断小数点
int power = 1;
if (s[i] == '.')
{
// 处理数据(小数点后的数据)
i++; // 跳过小数点
while (isdigit(s[i]))
{
val = val * 10 + (s[i] - '0');
power *= 10;
i++;
}
}
return sign * val / power;
}
P72 T 4-2
#include<stdio.h>
#include<ctype.h>
double atof(char s[]);
int main()
{
char s[100] = "123.4e-6";
double n = atof(s);
printf("%.8lf", n);
return 0;
}
// 优化版atof函数(能处理123.4e-6这种科学表示法)
double atof(char s[])
{
int i;
int sign = 1;
double val = 0.0;
// 空格跳过
for (i = 0; s[i] == ' '; i++)
;
// 判断符号
if (s[i] == '+' || s[i] == '-')
{
sign = (s[i] == '+' ? 1 : -1);
i++; // 跳过符号
}
// 处理数据(小数点前的数据)
while (isdigit(s[i]))
{
val = val * 10 + (s[i] - '0');
i++;
}
// 判断小数点
int power = 1;
if (s[i] == '.')
{
// 处理数据(小数点后的数据)
i++; // 跳过小数点
while (isdigit(s[i]))
{
val = val * 10 + (s[i] - '0');
power *= 10;
i++;
}
}
// 判断是否有e/E
int exp_val = 0;
int exp_sign = 1;
int exp_power = 1;
if (s[i] == 'e' || s[i] == 'E')
{
i++; // 跳过e/E
// 判断e/E后的符号
if (s[i] == '+' || s[i] == '-' || isdigit(s[i]))
{
exp_sign = ((s[i] == '+' || isdigit(s[i])) ? 1 : -1);
i += isdigit(s[i]) ? 0 : 1; // 跳过符号如果有的话
while (isdigit(s[i]))
{
exp_val = exp_val * 10 + (s[i++] - '0');
}
}
for (int k = 0; k < exp_val; k++)
{
exp_power *= 10;
}
}
return (exp_sign < 0) ? (sign * val / power / exp_power) : (sign * val / power * exp_power);
}
*:支持e后无数字的情况(123.4e)会输出(123.4),核心原因是因为exp_val = 0;
P75 4.3 逆波兰计算器
第一版(不支持小数和大于9的数)
#include<stdio.h>
#include<ctype.h>
#define MAXLINE 1000
double getop();
int push(double f);
double pop();
// 主入口,负责调用各个函数
int main()
{
double n = getop();
printf("%.2lf", n);
return 0;
}
// 入栈与弹出函数
// 两个函数共用一个栈
double val[MAXLINE];
int sp = 0; // 栈的索引
// push入栈函数
int push(double f)
{
if (sp < MAXLINE)
{
val[sp++] = f; // 入栈
return 0; // 正常结束,表示入栈成功
}
else
{
printf("错误:栈满,入栈失败\n");
return -1; // 入栈失败
}
}
// pop弹出函数
double pop() // 弹出一个数(先入后弹)
{
if (sp <= 0)
{
printf("错误:栈空,无数据弹出\n");
return -1; // 栈中无数据可供弹出
}
else // 说明栈中至少有一个数据可供弹出
{
return val[--sp];
}
}
// 数据输入与处理函数
double getop()
{
double op2;
char c;
while((c = getchar()) != '\n' && c != EOF)
{
if (isspace(c)) // 空格跳过
continue;
if (isdigit(c)) // 判断是否为数值
{
push(c - '0'); // 调用入栈函数入栈
}
else
{
switch (c)
{
// 从栈中弹出两个数,计算,结果压入栈中
case '+':
push(pop() + pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '*':
push(pop() * pop());
break;
case '/':
if ((op2 = pop()) == 0)
{
printf("错误:除零错误\n");
return 0.0;
}
push(pop() / op2);
break;
default :
printf("错误:非法字符 %c\n", c);
return 0.0; // 非法符号
}
}
}
return pop();
// printf("%lf", val[0]);
}
第二版(支持小数和大于9的数)
// 要求能处理输入的小数,以及大于9的数,将数据处理与数据逆波兰运算分开
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#define MAXLINE 1000
#define NUMBER '0'
char s[MAXLINE];
int getop();
int push(double f);
double pop();
// 逆波兰计算器
int main()
{
int type;
double op2;
while ((type = getop()) != '\n' && type != EOF)
{
switch (type)
{
// 数字入栈
case NUMBER:
push(atof(s));
break;
// 从栈中弹出两个数,计算,结果压入栈中
case '+':
push(pop() + pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '*':
push(pop() * pop());
break;
case '/':
if ((op2 = pop()) == 0)
{
printf("错误:除零错误\n");
return 1;
}
push(pop() / op2);
break;
default:
printf("错误:非法字符 %c\n", type);
return 1; // 非法符号
}
}
// 打印计算结果
printf("%.2lf", pop());
return 0;
}
// 入栈与弹出函数
// 两个函数共用一个栈
double val[MAXLINE];
int sp = 0; // 栈的索引
// push入栈函数
int push(double f)
{
if (sp < MAXLINE)
{
val[sp++] = f; // 入栈
return 0; // 正常结束,表示入栈成功
}
else
{
printf("错误:栈满,入栈失败\n");
return -1; // 入栈失败
}
}
// pop弹出函数
double pop() // 弹出一个数(先入后弹)
{
if (sp <= 0)
{
printf("错误:栈空,无数据弹出\n");
return 0.0; // 栈中无数据可供弹出
}
else // 说明栈中至少有一个数据可供弹出
{
return val[--sp];
}
}
// 数据输入 返回数据和运算符号
int getop()
{
char c;
int i = 0;
int dot = 0;
// 跳过空格
while ((c = getchar()) == ' ' || c == '\t')
;
// 处理数字
if (isdigit(c))
{
s[i++] = c;
// 循环读取连续数字
while (isdigit(c = getchar()) || (c == '.' && dot == 0))
{
if (c == '.')
{
dot = 1;
}
s[i++] = c;
}
s[i] = '\0';
ungetc(c, stdin);
return NUMBER;
}
if (c == '\n' || c == EOF)
{
return EOF;
}
return c;
}
P78 T 4-3
// 添加取模运算 支持负数运算
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<math.h>
#define MAXLINE 1000
#define NUMBER '0'
char s[MAXLINE];
int getop();
int push(double f);
double pop();
// 逆波兰计算器
int main()
{
int type;
double op2;
while ((type = getop()) != EOF)
{
switch (type)
{
// 数字入栈
case NUMBER:
push(atof(s));
break;
// 从栈中弹出两个数,计算,结果压入栈中
case '+':
push(pop() + pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '*':
push(pop() * pop());
break;
case '/':
if ((op2 = pop()) == 0)
{
printf("错误:除零错误\n");
return 1;
}
push(pop() / op2);
break;
case '%':
if ((op2 = pop()) == 0)
{
printf("错误:模零错误\n");
return 1;
}
push(fmod(pop(), op2));
break;
default:
printf("错误:非法字符 %c\n", type);
return 1; // 非法符号
}
}
// 打印计算结果
printf("%.2lf", pop());
return 0;
}
// 入栈与弹出函数
// 两个函数共用一个栈
double val[MAXLINE];
int sp = 0; // 栈的索引
// push入栈函数
int push(double f)
{
if (sp < MAXLINE)
{
val[sp++] = f; // 入栈
return 0; // 正常结束,表示入栈成功
}
else
{
printf("错误:栈满,入栈失败\n");
return -1; // 入栈失败
}
}
// pop弹出函数
double pop() // 弹出一个数(先入后弹)
{
if (sp <= 0)
{
printf("错误:栈空,无数据弹出\n");
return 0.0; // 栈中无数据可供弹出
}
else // 说明栈中至少有一个数据可供弹出
{
return val[--sp];
}
}
// 数据输入 返回数据和运算符号 (支持负数入栈)
int getop()
{
char c;
int i = 0;
int dot = 0;
// 跳过空格
while ((c = getchar()) == ' ' || c == '\t')
;
// 判断负号还是减号
if (c == '-')
{
char next = getchar();
// 如果next是EOF,直接返回'-',不要ungetc
if (next == EOF)
{
return '-';
}
// 下一个是数字/小数点 → 负号(负数)
if (isdigit(next) || next == '.')
{
s[i++] = '-';
c = next;
}
// 否则 → 减号运算符
else
{
ungetc(next, stdin);
return c;
}
}
// 正数/小数/负数后续部分
if (isdigit(c) || c == '.')
{
s[i++] = c;
if (c == '.')
dot = 1;
while (isdigit(c = getchar()) || (c == '.' && dot == 0))
{
s[i++] = c;
if (c == '.')
dot = 1;
}
s[i] = '\0'; // 字符串结束符
ungetc(c, stdin); // 回退多读的字符
return NUMBER; // 返回数字标记
}
if (c == '\n' || c == EOF)
{
return EOF;
}
return c;
}
P78 T 4-4
// 添加取模运算 支持负数运算 添加了一些栈操作命令 程序将在输入’$‘时结束运算,并打印结果(默认栈顶)
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<math.h>
#define MAXLINE 1000
#define NUMBER '0'
char s[MAXLINE];
int START = 1;
// 入栈与弹出函数
// 两个函数共用一个栈
double val[MAXLINE];
int sp = 0; // 栈的索引
int getop();
int push(double f);
double pop();
// 逆波兰计算器
int main()
{
int type;
double op2;
while (START)
{
while ((type = getop()) != EOF)
{
switch (type)
{
// 数字入栈
case NUMBER:
push(atof(s));
break;
// 从栈中弹出两个数,计算,结果压入栈中
case '+':
push(pop() + pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '*':
push(pop() * pop());
break;
case '/':
if ((op2 = pop()) == 0)
{
printf("错误:除零错误\n");
return 1;
}
push(pop() / op2);
break;
case '%':
if ((op2 = pop()) == 0)
{
printf("错误:模零错误\n");
return 1;
}
push(fmod(pop(), op2));
break;
// 新增命令
case 'p': // 打印栈顶元素
if (sp > 0)
{
printf("栈顶元素:%.2f\n", val[sp - 1]);
}
else
{
printf("错误:栈空\n");
}
break;
case 'd': // 复制栈顶元素
if (sp > 0)
{
push(val[sp - 1]);
}
else
{
printf("错误:栈空\n");
}
break;
case 's': // 交换栈顶两个元素
if (sp > 1)
{
double temp = val[sp - 1];
val[sp - 1] = val[sp - 2];
val[sp - 2] = temp;
}
else
{
printf("错误:栈不足两个元素\n");
}
break;
case 'c': // 清空栈
sp = 0; // 重置栈
printf("栈已清空\n");
break;
case '$': // 结束
START = 0;
break;
default:
printf("错误:非法字符 %c\n", type);
return 1; // 非法符号
}
}
}
// 打印计算结果
printf("结果:%.2lf", pop());
return 0;
}
// push入栈函数
int push(double f)
{
if (sp < MAXLINE)
{
val[sp++] = f; // 入栈
return 0; // 正常结束,表示入栈成功
}
else
{
printf("错误:栈满,入栈失败\n");
return -1; // 入栈失败
}
}
// pop弹出函数
double pop() // 弹出一个数(先入后弹)
{
if (sp <= 0)
{
printf("错误:栈空,无数据弹出\n");
return 0.0; // 栈中无数据可供弹出
}
else // 说明栈中至少有一个数据可供弹出
{
return val[--sp];
}
}
// 数据输入 返回数据和运算符号 (支持负数入栈)
int getop()
{
char c;
int i = 0;
int dot = 0;
// 跳过空格
while ((c = getchar()) == ' ' || c == '\t')
;
// 判断负号还是减号
if (c == '-')
{
char next = getchar();
// 如果next是EOF,直接返回'-',不要ungetc
if (next == EOF)
{
return '-';
}
// 下一个是数字/小数点 → 负号(负数)
if (isdigit(next) || next == '.')
{
s[i++] = '-';
c = next;
}
// 否则 → 减号运算符
else
{
ungetc(next, stdin);
return c;
}
}
// 正数/小数/负数后续部分
if (isdigit(c) || c == '.')
{
s[i++] = c;
if (c == '.')
dot = 1;
while (isdigit(c = getchar()) || (c == '.' && dot == 0))
{
s[i++] = c;
if (c == '.')
dot = 1;
}
s[i] = '\0'; // 字符串结束符
ungetc(c, stdin); // 回退多读的字符
return NUMBER; // 返回数字标记
}
if (c == '\n' || c == EOF)
{
return EOF;
}
return c;
}
P78 T 4-5
// 添加取模运算 支持负数运算 添加了一些栈操作命令 程序将在输入’$‘时结束运算,并打印结果(默认栈顶)可以访问sin exp pow函数
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<math.h>
#define MAXLINE 1000
#define NUMBER '0'
char s[MAXLINE];
int START = 1;
// 入栈与弹出函数
// 两个函数共用一个栈
double val[MAXLINE];
int sp = 0; // 栈的索引
int getop();
int push(double f);
double pop();
// 逆波兰计算器
int main()
{
int type;
double op2;
double op3;
while (START)
{
while ((type = getop()) != EOF)
{
switch (type)
{
// 数字入栈
case NUMBER:
push(atof(s));
break;
// 从栈中弹出两个数,计算,结果压入栈中
case '+':
push(pop() + pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '*':
push(pop() * pop());
break;
case '/':
if ((op2 = pop()) == 0)
{
printf("错误:除零错误\n");
return 1;
}
push(pop() / op2);
break;
case '%':
if ((op2 = pop()) == 0)
{
printf("错误:模零错误\n");
return 1;
}
push(fmod(pop(), op2));
break;
// 新增命令
case 'p': // 打印栈顶元素
if (sp > 0)
{
printf("栈顶元素:%.2f\n", val[sp - 1]);
}
else
{
printf("错误:栈空\n");
}
break;
case 'd': // 复制栈顶元素
if (sp > 0)
{
push(val[sp - 1]);
}
else
{
printf("错误:栈空\n");
}
break;
case 's': // 交换栈顶两个元素
if (sp > 1)
{
double temp = val[sp - 1];
val[sp - 1] = val[sp - 2];
val[sp - 2] = temp;
}
else
{
printf("错误:栈不足两个元素\n");
}
break;
case 'c': // 清空栈
sp = 0; // 重置栈
printf("栈已清空\n");
break;
case 'i':
push(sin(pop())); // x的正弦值
break;
case 'e':
push(exp(pop())); // 幂函数e^x
break;
case '^':
op2 = (int)pop(); // y
op3 = pop(); // x
if (op3 == 0 && op2 <= 0)
{
printf("错误:未定义行为!\n");
}
else
{
push(pow(op3, op2));
}
break;
case '$': // 结束
START = 0;
break;
default:
printf("错误:非法字符 %c\n", type);
return 1; // 非法符号
}
}
}
// 打印计算结果
printf("结果:%.2lf", pop());
return 0;
}
// push入栈函数
int push(double f)
{
if (sp < MAXLINE)
{
val[sp++] = f; // 入栈
return 0; // 正常结束,表示入栈成功
}
else
{
printf("错误:栈满,入栈失败\n");
return -1; // 入栈失败
}
}
// pop弹出函数
double pop() // 弹出一个数(先入后弹)
{
if (sp <= 0)
{
printf("错误:栈空,无数据弹出\n");
return 0.0; // 栈中无数据可供弹出
}
else // 说明栈中至少有一个数据可供弹出
{
return val[--sp];
}
}
// 数据输入 返回数据和运算符号 (支持负数入栈)
int getop()
{
char c;
int i = 0;
int dot = 0;
// 跳过空格
while ((c = getchar()) == ' ' || c == '\t')
;
// 判断负号还是减号
if (c == '-')
{
char next = getchar();
// 如果next是EOF,直接返回'-',不要ungetc
if (next == EOF)
{
return '-';
}
// 下一个是数字/小数点 → 负号(负数)
if (isdigit(next) || next == '.')
{
s[i++] = '-';
c = next;
}
// 否则 → 减号运算符
else
{
ungetc(next, stdin);
return c;
}
}
// 正数/小数/负数后续部分
if (isdigit(c) || c == '.')
{
s[i++] = c;
if (c == '.')
dot = 1;
while (isdigit(c = getchar()) || (c == '.' && dot == 0))
{
s[i++] = c;
if (c == '.')
dot = 1;
}
s[i] = '\0'; // 字符串结束符
ungetc(c, stdin); // 回退多读的字符
return NUMBER; // 返回数字标记
}
if (c == '\n' || c == EOF)
{
return EOF;
}
return c;
}
P90 T 4-14
#include<stdio.h>
#define swap(t, x, y) { t temp = x; x = y; y = temp;}
int main()
{
int x = 5;
int y = 10;
printf("Before swap: x = %d, y = %d\n", x, y);
swap(int, x, y);
printf("After swap: x = %d, y = %d\n", x, y);
return 0;
}
第五章
P97 T 5-1
#include<stdio.h>
#include<ctype.h>
int getint(int* p);
int main()
{
int arr[5];
for (int i = 0; i < 5 && getint(&arr[i]) != EOF; i++)
;
for (int i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
int getint(int* p)
{
int c, sign;
while (isspace(c = getchar()))
;
if (!isdigit(c) && c != EOF && c != '+' && c != '-')
{ // 不是数
ungetc(c, stdin);
return 0;
}
sign = (c == '-') ? -1 : 1;
if (c == '-' || c == '+')
c = getchar(); // 获取下一个字符
if (!isdigit(c))
{
ungetc(sign < 0 ? '-' : '+', stdin);
return 0;
}
for (*p = 0 ; isdigit(c); c = getchar())
{
*p = *p * 10 + (c - '0');
}
*p *= sign;
if (c != EOF)
{
ungetc(c, stdin);
}
return c;
}
P97 T 5-2
#include<stdio.h>
#include<ctype.h>
float getfloat(float* p);
int main()
{
float arr[5];
for (int i = 0; i < 5 && getfloat(&arr[i]) != EOF; i++)
;
for (int i = 0; i < 5; i++)
{
printf("%.2f ", arr[i]);
}
return 0;
}
float getfloat(float* p)
{
int c, sign;
float pow = 1.0;
while (isspace(c = getchar()))
;
if (!isdigit(c) && c != EOF && c != '+' && c != '-')
{ // 不是数
ungetc(c, stdin);
return 0.0;
}
sign = (c == '-') ? -1 : 1;
if (c == '-' || c == '+')
c = getchar(); // 获取下一个字符
if (!isdigit(c))
{
ungetc(c, stdin);
return 0.0;
}
// 读取整数部分
for (*p = 0; isdigit(c); c = getchar())
{
*p = *p * 10 + (c - '0');
}
// 处理小数部分
if (c == '.')
{
c = getchar();
for (; isdigit(c); c = getchar())
{
*p = *p * 10 + (c - '0');
pow *= 10;
}
}
*p /= pow;
*p *= sign;
if (c != EOF)
{
ungetc(c, stdin);
}
return c;
}
P106 T 5-3
#include<stdio.h>
void strcat(char* s, char* t);
int main()
{
char s[14] = "Hello";
char t[8] = "_World!";
printf("修改前:%s\n", s);
strcat(s, t);
printf("修改后:%s\n", s);
return 0;
}
// 将t指向的字符串复制到s字符串的尾部
void strcat(char* s, char* t)
{
// 得到s字符串的末尾指针(指向‘\0’)
while (*s != '\0')
s++;
// 复制(包括“\0”)
while (*s++ = *t++)
;
}
P106 T 5-4
#include<stdio.h>
int strend(char* s, char* t);
int main()
{
char s[] = "would";
char t[] = "ld";
printf("%d", strend(s, t));
return 0;
}
// 如果字符串t出现在字符串s的尾部,函数返回1,否则返回0
int strend(char* s, char* t)
{
// 注意点,要求完全符合
// 可以从后往前比较
// 得到字符串s的尾部‘\0’位置处指针
char* end_s = s;
for ( ; *end_s != '\0'; end_s++)
;
// 得到字符串t的尾部‘\0’位置处指针
char* end_t = t;
for (; *end_t != '\0'; end_t++)
;
// 比较
while (end_t > t)
{
if (*--end_s != *--end_t)
return 0;
}
return 1;
}
P118 T 5-10
// 计算从命令行中输入的逆波兰表达式的值,其中每个运算符或操作数用一个单独的参数表示 例如 5 3 2 - * 输出结果为4即:(5-2)* 2 = 4
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#define MAXLINE 1000
int sp = 0; // 栈的索引
int push(double f);
double pop();
// 逆波兰计算器
int main(int argc, char* argv[])
{
int c;
double op2;
while (--argc > 0)
{
++argv;
c = (*argv)[0];
if (isdigit(c) || c == '.' ||
(c == '-' && (isdigit((*argv)[1]) || ((*argv)[1]) == '.')))
{
push(atof(*argv));
}
else
{
if (sp < 2)
{
printf("错误:操作数不足\n");
return 1;
}
switch (c)
{
// 从栈中弹出两个数,计算,结果压入栈中
case '+':
push(pop() + pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '*':
push(pop() * pop());
break;
case '/':
if ((op2 = pop()) == 0)
{
printf("错误:除零错误\n");
return 1;
}
push(pop() / op2);
break;
default:
printf("错误:非法字符 %s\n", *argv);
return 1; // 非法符号
}
}
}
if (sp != 1)
{
printf("错误:表达式不合法\n");
return 1;
}
// 打印计算结果
printf("%.2lf", pop());
return 0;
}
// 入栈与弹出函数
// 两个函数共用一个栈
double val[MAXLINE];
// push入栈函数
int push(double f)
{
if (sp < MAXLINE)
{
val[sp++] = f; // 入栈
return 0; // 正常结束,表示入栈成功
}
else
{
printf("错误:栈满,入栈失败\n");
return -1; // 入栈失败
}
}
// pop弹出函数
double pop() // 弹出一个数(先入后弹)
{
if (sp <= 0)
{
printf("错误:栈空,无数据弹出\n");
return 0.0; // 栈中无数据可供弹出
}
else // 说明栈中至少有一个数据可供弹出
{
return val[--sp];
}
}
P118 T 5-13
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAXLINES 5000
#define MAXLINE 1000
int getline(char s[], int lim);
int readline(char* arr[], int maxlines);
void writetail(char* arr[], int nline, int n);
int main(int argc, char* argv[])
{
int n = 10; // 默认参数 n = 10
int nline = 0; // 行数
// 定义一个二维数组,存储输入的每行文字
char* arr[MAXLINES];
// 获取参数n
if (argc > 1 && argv[1][0] == '-')
{
n = atoi(argv[1] + 1);
}
// 将输入存储到指针数组中
nline = readline(arr, MAXLINES);
// 输出最后n行(检查特殊情况)
writetail(arr, nline, n);
return 0;
}
int getline(char s[], int lim)
{
int c, i;
for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; i++)
s[i] = c;
if (c == '\n')
{
s[i] = c;
i++;
}
s[i] = '\0';
return i;
}
int readline(char* arr[], int maxlines)
{
int nline = 0; // 行数初始化为0
int len = 0;
char s[MAXLINE]; // 定义一个缓存字符数组
while ((len = getline(s, MAXLINE)) > 0)
{
// 去掉换行符
s[len - 1] = '\0';
// 申请内存空间
char* p = malloc(len);
strcpy(p, s);
arr[nline++] = p; // 存储到指针数组中
}
return nline; // 返回行数
}
void writetail(char* arr[], int nline, int n)
{
int start = 0;
/* 先排查特殊情况 1)n > nline 2) nline == 0 */
if (nline == 0)
{
printf("无输入\n");
return;
}
if (n > nline)
{
n = nline;
}
start = nline - n;
printf("最后%d行为:\n", n);
for (int i = start; i < nline; i++)
{
printf("%s\n", arr[i]);
}
}
第六章
P138 6.5
从这里开始,代码风格({}位置改为和Java风格一致)
#include<stdio.h>
#include<ctype.h>
#include<stdlib.h>
#include<string.h>
#define MAXLINE 50
// 定义一个结构体存储word count 以及left right
typedef struct tnote {
char* word;
int count;
struct tnote* left;
struct tnote* right;
} Tnote;
int getword(char* word, int lim);
Tnote* addtree(Tnote* p, char* w);
void treeprint(Tnote* p);
int main() {
char w[MAXLINE];
Tnote* root = NULL;
while (getword(w, MAXLINE) != EOF)
if (isalpha(w[0]))
root = addtree(root, w);
treeprint(root);
return 0;
}
// 创建二叉树
Tnote* addtree(Tnote* p, char* w) {
int cond;
/* 第一次添加的数据默认为根,之后每次比较后出现NULL的情况,就创建新的节点 */
if (p == NULL) {
p = malloc(sizeof(Tnote)); /* 新节点 */
char* w1 = malloc(strlen(w) + 1); /* 给得到的字符串申请一块内存空间 */
strcpy(w1, w); /* 将得到的字符串复制到新的内存地址 */
p -> word = w1; /* 给节点赋值 */
p -> count = 1;
p -> left = p -> right = NULL; /* 左右孩子默认为空 */
}
else if ((cond = strcmp(w, p->word)) == 0)
p -> count++;
else if (cond < 0) {
p -> left = addtree(p -> left, w); /* 递归调用函数 */
}
else {
p -> right = addtree(p -> right, w);
}
return p;
}
// 中序遍历
void treeprint(Tnote* p) {
/* 左 根 右 */
if (p != NULL) {
treeprint(p -> left);
printf("%d %s\n", p -> count, p -> word);
treeprint(p -> right);
}
}
// 获取单词
int getword(char* word, int lim) {
char* w = word;
int c;
while (isspace(c = getchar()))
;
if (c != EOF)
*w++ = c;
if (!isalpha(c)) {
*w = '\0';
return c;
}
for (; --lim > 0; w++) {
if (!isalnum(*w = getchar())) {
ungetc(*w, stdin);
break;
}
}
*w = '\0';
return word[0];
}
P143 T 6-4(第一版 单词树-频率树)
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 20
typedef struct wordnode {
char* word;
int count;
struct wordnode* left;
struct wordnode* right;
} Wnode;
/* 单词树 */
Wnode* insertw(Wnode* root, char* word) {
if (root == NULL) {
root = malloc(sizeof(Wnode));
root -> word = malloc(strlen(word) + 1);
strcpy(root -> word, word);
root -> count = 1;
root -> left = root -> right = NULL;
return root;
}
int n = strcmp(word, root -> word);
if (n == 0)
root -> count++;
else if (n < 0)
root -> left = insertw(root -> left, word);
else
root -> right = insertw(root -> right, word);
return root;
}
typedef struct countnode {
int count;
char* word;
struct countnode* left;
struct countnode* right;
} Cnode;
/* 频率树 */
Cnode* insertc(Cnode* root, int value, char* word) {
if (root == NULL) {
root = malloc(sizeof(Cnode));
root -> count = value;
root -> word = malloc(strlen(word) + 1);
strcpy(root -> word, word);
root -> left = root -> right = NULL;
return root;
}
int n = value - root -> count;
if (n == 0) {
if (strcmp(root -> word, word) < 0)
root -> left = insertc(root -> left, value, word);
else
root -> right = insertc(root -> right, value, word);
}
else if (n < 0)
root -> left = insertc(root -> left, value, word);
else
root -> right = insertc(root -> right, value, word);
return root;
}
/* 遍历单词树得到频率和单词,并创建频率树 */
Cnode* wordorder_creatcounttree(Wnode* tree, Cnode* counttree) {
if (tree == NULL)
return counttree;
counttree = wordorder_creatcounttree(tree->left, counttree);
counttree = insertc(counttree, tree->count, tree->word);
counttree = wordorder_creatcounttree(tree->right, counttree);
return counttree;
}
/* 右 根 左 遍历频率树 */
void countorder(Cnode* tree) {
if (tree == NULL)
return;
countorder(tree -> right);
printf("%d %s\n", tree -> count, tree -> word);
countorder(tree -> left);
}
/* 获取输入的单词 */
int getword(char* word) {
int c;
while (isspace((c = getchar())))
;
if (c == EOF)
return EOF;
if (!isalpha(c)) {
word[0] = c;
word[1] = '\0';
return c;
}
int i = 0;
word[i++] = c;
while (i < MAX - 1 && isalnum(c = getchar()))
word[i++] = c;
word[i] = '\0';
if (c != EOF)
ungetc(c, stdin);
return word[0];
}
int main() {
char word[MAX];
int c;
Wnode* wordtree = NULL;
Cnode* counttree = NULL;
while ((c = getword(word)) != EOF) {
if (isalnum(word[0])) {
wordtree = insertw(wordtree, word);
}
}
counttree = wordorder_creatcounttree(wordtree, counttree);
countorder(counttree);
return 0;
}
P143 T 6-4(第二版 单词树-数组)
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define MAXLINE 1000
typedef struct wordnode {
char* word;
int count;
struct wordnode* left;
struct wordnode* right;
} wnode;
wnode* insert(wnode* root, char* word) {
if (root == NULL) {
root = malloc(sizeof(wnode));
root -> word = malloc(strlen(word) + 1);
strcpy(root -> word, word);
root -> count = 1;
root -> left = root -> right = NULL;
return root;
}
int n = strcmp(word, root -> word);
if (n == 0)
root -> count++;
else if (n < 0)
root -> left = insert(root -> left, word);
else
root -> right = insert(root -> right, word);
return root;
}
int getword(char* w) {
int c, i;
while (isspace(c = getchar()))
;
if (c == EOF)
return c;
if (!isalpha(c)) {
w[0] = c;
w[1] = '\0';
return c;
}
w[0] = c;
for (i = 1; isalnum(c = getchar()); i++)
w[i] = c;
w[i] = '\0';
if (c != EOF)
ungetc(c, stdin);
return w[0];
}
/* 中序遍历得到wnode类型的数组(写法一) */
wnode arr[MAXLINE];
int n = 0;
void inorder(wnode* tree) {
if (tree == NULL)
return;
inorder(tree -> left);
arr[n].word = malloc(strlen(tree->word) + 1);
strcpy(arr[n].word, tree -> word);
arr[n].count = tree -> count;
n++;
inorder(tree -> right);
}
/* 中序遍历得到wnode类型的数组(写法二) */
void inorder2(wnode* tree, wnode arr[], int *n) {
if (tree == NULL)
return;
inorder2(tree -> left, arr, n);
arr[*n].word = tree -> word;
arr[*n].count = tree -> count;
(*n)++;
inorder2(tree -> right, arr, n);
}
/* inorder2的干净接口 */
int _inorder2(wnode* tree, wnode arr[]) {
int n = 0;
inorder2(tree, arr, &n);
return n;
}
/* cmp函数 */
int cmp(const void* a, const void* b) {
wnode* x = (wnode*)a;
wnode* y = (wnode*)b;
if (x -> count != y -> count)
return x -> count < y -> count ? 1 : -1;
return strcmp(y -> word, x -> word);
}
/*释放*/
void freetree(wnode* tree) {
if (tree == NULL)
return;
freetree(tree->left);
freetree(tree->right);
free(tree->word);
free(tree);
}
int main() {
char word[100];
wnode* tree = NULL;
wnode arr1[MAXLINE];
while ( getword(word) != EOF) {
if (isalpha(word[0]))
tree = insert(tree, word);
}
int n = _inorder2(tree, arr1);
/* 对数组排序,降序打印 */
qsort(arr1, n, sizeof(wnode), cmp);
for (int i = 0; i < n; i++)
printf("%d %s\n", arr1[i].count, arr1[i].word);
freetree(tree);
return 0;
}
P145 T 6-5
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct nlist {
struct nlist* next;
char* name;
char* defn;
};
#define HASHSIZE 101
static struct nlist* hashtab[HASHSIZE];
unsigned hash(char* s) {
unsigned hashval;
for (hashval = 0; *s != '\0'; s++) {
hashval = *s + 31 * hashval;
}
return hashval % HASHSIZE;
}
struct nlist* lookup(char* s) {
struct nlist* np;
for (np = hashtab[hash(s)]; np != NULL; np = np -> next )
if (strcmp(np -> name, s) == 0)
return np;
return NULL;
}
void undef(char* s) {
struct nlist* np,* prev;
unsigned hashval = hash(s);
np = hashtab[hashval];
prev = NULL;
while (np != NULL) {
if (strcmp(s, np -> name) == 0) {
if (prev == NULL) {
hashtab[hashval] = np -> next;
}
else {
prev -> next = np -> next;
}
free(np -> name);
free(np -> defn);
free(np);
return;
}
prev = np;
np = np -> next;
}
}
P145 T 6-6
从这里开始*的位置将沿用K&R代码风格(即解引用符号紧跟变量名)
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct nline {
struct nline *next;
char *name;
char *defn;
};
#define HASHSIZE 101
static struct nline *hashtab[HASHSIZE];
/* 实现字符串转数字 */
unsigned hash(char *s) {
unsigned hashval = 0;
while (*s != '\0') {
hashval = *s + 31 * hashval;
s++;
}
return hashval % HASHSIZE;
}
/* 查找 */
struct nline *lookup(char *s) {
struct nline *np;
for (np = hashtab[hash(s)]; np != NULL; np = np -> next)
if (strcmp(s, np -> name) == 0)
return np;
return NULL;
}
/* 插入哈希表 */
struct nline *insert(char *name, char *defn) {
unsigned hashval;
struct nline *np;
if ((np = lookup(name)) == NULL) { /* 查找为空 */
np = (struct nline *)malloc(sizeof(*np));
if (np == NULL) /* 防止malloc函数没有申请到空间的情况,下方同理 */
return NULL;
np -> name = strdup(name);
if (np -> name == NULL)
return NULL;
hashval = hash(name);
np -> next = hashtab[hashval]; /* 新插入的元素的next要指向旧元素 */
hashtab[hashval] = np; /* 将新插入的元素设为对头 */
}
else /* 已经存在,更新defn */
free(np -> defn);
np -> defn = strdup(defn); /* 更新defn(无论是否查到name,我们都要更新defn,索性就将defn的更新放在末尾) */
if (np -> defn == NULL)
return NULL;
return np;
}
/* 获取输入单词 */
int getword(char *s, int lim) {
/* 我们要加入对#定义的获取 */
int c, i;
while (isspace(c = getchar())) /* 跳过空格 */
;
if (c == EOF)
return c;
if (isalpha(c) || c == '_') { /* 处理字母和下划线 */
s[0] = c;
i = 1;
while (i < lim - 1 && isalnum(c = getchar())) {
s[i++] = c;
}
if (c != EOF)
ungetc(c, stdin);
s[i] = '\0';
return s[0];
}
if (isdigit(c)) { /* 处理数字 */
s[0] = c;
i = 1;
while (i < lim - 1 && isalnum(c = getchar())) {
s[i++] = c;
}
if (c != EOF)
ungetc(c, stdin);
s[i] = '\0';
return s[0];
}
/* 其他字符(这里负责捕捉#) */
s[0] = c;
s[1] = '\0';
return c;
}
#define MAXWORD 100
int main() {
char word[MAXWORD];
char name[MAXWORD];
char defn[MAXWORD];
struct nline *np;
while (getword(word, MAXWORD) != EOF) {
if (strcmp("#", word) == 0) {
getword(word, MAXWORD);
if (strcmp("define", word) == 0) {
getword(name, MAXWORD); /* 下一个单词是宏名 */
getword(defn, MAXWORD); /* 下一个单词是替换内容 */
insert(name, defn);
}
}
else if (isalpha(word[0])) {
if ((np = lookup(word)) != NULL)
printf("%s ", np -> defn);
else
printf("%s ", word);
}
else
printf("%s ", word);
}
return 0;
}
至此,我的第二次C语言学习就此结束,书中后的两章内容对我之后的发展作用不大,因此略过。
对于我自己编写的这套所谓的参考答案,参考价值并不高,我在写这些习题的时候,很大一部的纠错是得益于当今AI的迅速发展,在这里非常感谢豆包和GPT两位老师的帮助,没有这两位老师,我将不可能完成这对我来说近乎不可能的任务。
因此希望有提前看到这里的读者,善用AI,但如果真的是因为某些不可抗拒因素导致无法使用AI,再将我所编写的所谓的参考答案作为参考。
该参考答案我不能保证是最优解,但是我有九成的把握保证这些答案是正确的,能跑通的。
啊?居然还没有达到一万字吗?可恶~~~
“莫道桑榆晚,未霞尚满天。”

浙公网安备 33010602011771号