水仙花数
如果一个四位数等于它的每一位数的4次方之和,则称为玫瑰花数,比如1634=1^4+6^4+3^4+4^4,编程输出所有的玫瑰花数。
#include<stdio.h>
// 判断数n是不是玫瑰花数
int isRose(int n) {
int sum = 0; // 各位数四次方和
int tmp = n; // 拷贝
while (n) {
int m = n % 10; // 取出低位
sum += m*m*m*m; // 维护sum
n /= 10;
}
return sum == tmp;
}
int main() {
printf("玫瑰花数:\n");
for (int i = 1000; i <= 9999; i++) { // 遍历所有的四位数
if (isRose(i)) printf("%d\n",i);
}
return 0;
【小结】此题涉及到了循环语句,通过不断取整求余来获取每一位的值。由于对n进行了一系列操作,故其值将随之发生改变,需要额外定义一个临时变量tmp来存储其原值,便于后续比较。
完数问题
一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如6=1+2+3。编程找出1000以内的所有完数。
#include<stdio.h>
int isPerfect(int n) {
int sum = 0; // 因子和
for (int i = 1; i < n; i++) {
if (n % i == 0) { // 判断是否为因子
sum += i; // 维护sum
}
}
return sum == n;
}
int main() {
printf("完数:\n");
for (int i = 1; i <= 1000; i++) { // 遍历1~1000
if (isPerfect(i)) printf("%d\n",i);
}
return 0;
}
【小结】此题同玫瑰花数大同小异。需注意在求因子时,因子必须小于其本身。
转方阵
对一个方阵转置,就是把原来的行号变列号,原来的列号变行号。例如,图1的方阵转置后变为图2;但如果是对该方阵顺时针旋转(不是转置),却是如图3。请编写一个函数,实现一个方阵顺时针旋转。注:方阵是一个N*N的矩阵。
#include<stdio.h>
#define N 4 // 方阵的大小
void printMatrix(int matrix[][N]) {
for (int i = 0; i < N; i++) { // 遍历行号
for (int j = 0; j < N; j++) { // 遍历列号
printf("%d\t", matrix[i][j]);
}
printf("\n"); // 输出完一行后换行
}
}
void rotate(int matrix[][N]) {
// 转置矩阵
for (int i = 0; i < N; i++) {
for (int j = i; j < N; j++) { // j = i,防止重复转置最终抵消
// 行号变列号,列号变行号
int tmp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = tmp;
}
}
// 反转矩阵的列
for (int i = 0; i < N; i++) { // 遍历每一行
int start = 0;
int end = N - 1;
while (start < end) {
// 交换列号
int tmp = matrix[i][start];
matrix[i][start] = matrix[i][end];
matrix[i][end] = tmp;
start++;
end--;
}
}
}
int main() {
int A[N][N] = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,16}
};
printf("原矩阵:\n");
printMatrix(A);
rotate(A);
printf("\n顺时针旋转后的矩阵:\n");
printMatrix(A);
return 0;
}
【小结】为了输出矩阵,编写了辅助函数printMatrix()。将顺时针旋转拆解为两步——先转置,再交换列。在转置的过程中,应注意内层循环,避免重复操作相互抵消。在交换列的过程中,应注意到只需对一半的列进行交换操作。如若对所有列都进行交换操作,会导致两次操作相互抵消。
链表操作
建立单向int链表,连续输入5个结点创建链表,并实现在原链表中插入数字、删除数字、查找数字的功能。
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data; // 数据域
struct Node* next; // 指针域
};
Node* head; // 头结点
Node* rear; // 尾结点
// 链表初始化
void init() {
head = (Node*)malloc(sizeof(Node));
rear = head; // 尾结点指向头指针
rear->next = NULL; // 尾结点指向空
}
// 插入数字 ——尾插
void insert(int num) {
// 创建新结点p
Node* p = (Node*)malloc(sizeof(Node));
p->data = num;
p->next = NULL;
rear->next = p; // 尾结点指向新结点
rear = rear->next; // 更新尾结点
}
int find(int num) { // 查找数字
Node* str = head; // 查找指针指向头结点
int ind = 0;
while (str->next) { // 当查找指针指向的不是尾结点
str = str->next; // 移动查找指针
if (str->data == num) return ind; // 找到后返回索引值(从0开始)
else ind++;
}
return -1; // 查找到尾结点,说明不存在
}
void delet(int num) { // 删除数字
Node* pre = head; // 找到待删除结点的前驱结点
int ind = find(num);
for (int i = 0;i < ind; i++) {
pre = pre->next;
}
pre->next = pre->next->next;
}
void printLink() {
Node* str = head;
while (str->next != rear) {
str = str->next;
printf("%d -> ", str->data);
}
str = str->next;
printf("%d\n", str->data);
}
void menu() {
printf("1.插入数字\n");
printf("2.删除数字\n");
printf("3.查找数字\n");
printf("4.打印链表\n");
printf("5.退出\n");
}
int main() {
init(); // 链表初始化
int n = 5;
int num = 0;
printf("请输入5个要插入的数:\n");
while (n--) { // 循环输入
scanf("%d", &num);
insert(num);
}
menu();
int operation = 0;
while (1) {
int ind = 0; // 辅助查找
printf("请选择功能:");
scanf("%d", &operation);
switch (operation) {
case 1:
printf("要插入的数:");
scanf("%d", &num);
insert(num);
break;
case 2:
printf("要删除的数:");
scanf("%d", &num);
if (!find(num)) { // 数不在链表
printf("未找到数%d\n", num);
} else { // 数在链表
delet(num);
}
break;
case 3:
printf("要查找的数:");
scanf("%d", &num);
ind = find(num);
if (ind != -1) {
printf("在链表第%d个结点\n", ind + 1);
} else {
printf("数不在链表内\n");
}
break;
case 4:
printLink();
break;
case 5:
return 0;
}
}
return 0;
}
【小结】此题涉及到结构体的定义、指针的运用和数据结构链表的基本操作内容。在对链表进行增删改查时,需要注意指针指向,避免出现数据泄漏引发异常。(代码写得略为冗余,不喜勿喷)
统计频率
读入一个文件,文件中包含字母,数字,空格,标点符号等。请统计文件中的字母,数字,空格和其他符号的数目,在屏幕上显示。
#include<stdio.h>
#include<string.h>
int main() {
int letter = 0, num = 0, space = 0, other = 0; // 定义四个变量存储个数
FILE *file;
file = fopen("Test1.txt", "r"); // 打开文件,设置权限为可读
char ch[100000];
fgets(ch, 100000, file); // 读入字符串
fclose(file); // 关闭文件
// 遍历字符串,根据ASCII码进行计数
for (int i = 0; ch[i]; i++) {
if ((ch[i] >= 'a' && ch[i] <= 'z') || (ch[i] >= 'A' && ch[i] <= 'Z')) letter++;
else if (ch[i] >= '0' && ch[i] <= '9') num++;
else if (ch[i] == ' ') space++;
else if (ispunct(ch[i])) other++;
}
// 打印统计结果
printf("字母个数:%d\n", letter);
printf("数字个数:%d\n", num);
printf("空格个数:%d\n", space);
printf("标点符号:%d\n", other);
return 0;
}
【小结】通过文件指针读取文本,逐个字符进行判断即可。需要注意使用 ispunct(char ch) 函数来判断是否为标点符号,不能简单的认为除去其他类,剩下的均为标点符号。
统计字母个数
读取文件中的字符串,统计从“a”到“z”26个字母各自出现的次数,并将结果放入数组中。如文件中有字符串abcdefgabcdeabc,输出 33322110000000000000000000。
#include<stdio.h>
int cnt[26];
int main() {
FILE *file;
file = fopen("Test2.txt","r"); // 打开文件,设置权限为可读
char ch[100000]; // 定义数组存放文件字符串
fscanf(file, "%s", ch); // 获取字符串
// 遍历计数
for (int i = 0; ch[i]; i++) {
cnt[ch[i] - 'a']++;
}
// 遍历输出
for (int i = 0; i < 26; i++) {
printf("%d",cnt[i]);
}
fclose(file); // 关闭文件
return 0;
}
【小结】此题涉及到数组的使用、ASCII码的运算、文件操作等内容。需要注意数组的索引从0开始,隐式地对应字母“a”,故在通过ASCII码运算得到索引值时仅需减去“a”,无须再加1。