C程序设计语言实习(基础)

水仙花数

如果一个四位数等于它的每一位数的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。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值