1、BoBo教KiKi字符常量或字符变量表示的字符在内存中以ASCII码形式存储。BoBo出了一个问题给KiKi,转换以下ASCII码为对应字符并输出他们。
//73,32,99,97,110,32,100,111,32,105,116,33
int main() {
int arr[] = {73,32,99,97,110,32,100,111,32,105,116,33};
int i = 0;
//sizeof(arr)- 计算的是数组的总大小,单位是字节
//sizeof(arr[0]) - 计算的是数组元素的大小
int sz = sizeof(arr) / sizeof(arr[0]);
while (i < sz)
{
printf("%c", arr[i]);
i++;
}
return 0;
}
2、输入一个人的出生日期(包括年月日),将该生日中的年、月、日分别输出。
数据范围:年份满足 1990≤y≤2015,月份满足 1≤m≤12 ,日满足 1≤d≤30 。
输入描述:输入只有一行,出生日期,包括年月日,年月日之间的数字没有分隔符。
输出描述:三行,第一行为出生年份,第二行为出生月份,第三行为出生日期。输出时如果月份或天数为1位数,需要在1位数前面补0
输入:20130225
输出:
year=2013
month=02
date=25
备注:
通过scanf函数的%m格式控制可以指定输入域宽,输入数据域宽(列数),
按此宽度截取所需数据;通过printf函数的%0格式控制符,输出数值时指定左面不使用的空位置自动填0。
int main() {
//输入数据
int year=0;
int month=0;
int date=0;
scanf("%4d%2d%2d",&year,&month,&date);
printf("year=%d\n",year);
//通过printf函数的%0格式控制符,输出数值时指定左面不使用的空位置自动填0
printf("month=%02d\n",month);//%02d,2d的意思是要打印两位,0的意思是我要拿0补齐两位,写%2d的时候输入1,会显示“空格1”,写%02d会显示01
printf("date=%02d\n",date);
}
3、依次输入一个学生的学号,以及3科(C语言,数学,英语)成绩,在屏幕上输出该学生的学号,3科成绩(注:输出成绩时需进行四舍五入且保留2位小数)。
数据范围:学号满足 1≤n≤20000000 1≤n≤20000000 ,各科成绩使用百分制,且不可能出现负数
输入描述:
学号以及3科成绩,学号和成绩之间用英文分号隔开,成绩之间用英文逗号隔开。
输出描述:
学号,3科成绩,输出格式详见输出样例。
输入:
17140216;80.845,90.55,100.00
输出:
The each subject score of No. 17140216 is 80.85, 90.55, 100.00.
int main() {
int id = 0;
float c = 0.0f;//如果直接写0.0会被当做double类型,加上f就是float类型
float math = 0.0f; //0.0是个小数,如果后面没有f,0.0就是double类型,但是前面是个float,所以要在后面加个f变成float类型
float eng = 0.0f;
//输入
scanf("%d;%f,%f,%f", &id, &c, &math, &eng);
//输出
printf("The each subject score of No. %d is %.2f, %.2f, %.2f.\n", id, c, math, eng);//.2f表示小数点后保留两位
return 0;
}
4、小乐乐找最大数(打擂台)
小乐乐获得4个最大数,请帮他编程找到最大的数。
输入描述:一行,4个整数,用空格分开。
输出描述:一行,一个整数,为输入的4个整数中最大的整数。
输入:5 8 2 5
输出:8
//找最大数的通用办法(写法1)
int main() {
int arr[4] = { 0 };
//0 1 2 3
int i = 0;
while (i < 4)
{
scanf("%d", &arr[i]);//存四个数
i++;
}
//找max(打擂台)
//假设第一个元素就是最大值
int max = arr[0];
i = 1;
while (i < 4)
{
if (arr[i] > max)
{
max = arr[i];
}
i++;
}
printf("%d\n", max);
return 0;
}
//找最大数(写法二)【可以不存起来】
int main() {
int i=1;
int n = 0;
int max = 0;
//假设第一个元素就是max
scanf("%d", &max);
while (i < 4)
{
scanf("%d", &n);
if (n > max)
max = n;
i++;
}
printf("%d\n", max);
}
5、分支与循环部分练习(涉及二分法练习)【阶乘、阶乘之和、二分查找、字符中间汇聚、模拟登录】
//1、计算n的阶乘
//1*2*3...*n,要产生1~n个数字,可以用循环(练思维就行,太大的数字不行)
int main() {
int i = 1;
int n = 0;
int ret = 1;
scanf("%d", &n);
for (i = 1;i <= n;i++)
{
ret = ret * i;//累乘
}
printf("%d\n", ret);
return 0;
}
//===========================================================
//2、计算1!+2!+3!+...+10!(求和)
//1
//1*2
//1*2*3
//1*2*3*4
int main() {
int i = 1;
int ret = 1;
int n = 1;
int sum = 0;
for (n = 1;n <= 10;n++)
{
ret = 1;
for (i = 1;i <= n;i++)
{
ret = ret * i;
}
sum = sum + ret;
}
printf("%d\n", sum);
return 0;
}
//优化(只用了一层for循环)
int main(){
int ret = 1;
int n = 1;
int sum = 0;
for (n = 1;n <= 10;n++)
{
ret = ret * n;//ret=1 n=1、 n=2 ret=1*2=2、 n=3 ret=2*3=6
sum = sum + ret;//sum=0+1=1、 sum=1+2、 sum=1+2+6...
}
printf("%d\n", sum);
return 0;
}
//===========================================================
//3、在一个有序数组中查找具体的某个数字n。(二分查找)
//3-1、不用二分查找,直接遍历(效率慢)
int main() {
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int k = 7;//要查找的数字
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0;i <= sz;i++)
{
if (arr[i] == k)
{
printf("找到了,下标是:%d", i);
}
}
if (i == sz) {
printf("Not found!");
}
return 0;
}
//3-2、二分查找(猜数字)/折半查找【前提肯定是有序数组】
//left(0) 1 2 3 4 5 6 7 8 9 10 right(9)——(9+0)/2=4,即5
//left(5)6 7 8 9 10 right(9)—(9+5)/2=7(mid),即8
int main() {
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 7;//要查找的数字
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int left = 0;
int right = sz - 1;
while (left <= right) {
int mid = (left + right) / 2; //在循环体里面
//如果担心 (left + right) / 2 的结果超出int所能表示的最大值,就会溢出。可以写成:
//int mid=left+(right-left)/2 ——》 想:一个高台阶一个低台阶,怎么样才能变成一样高。这样算出的平均值不会超出范围
if (arr[mid] < k)
{
left = mid + 1;
}
else if (arr[mid] > k)
{
right = mid - 1;
}
else
{
printf("找到了!下标是:%d", mid);
break;
}
}
if (left > right) {
printf("Not found!");
}
return 0;
}
//===========================================================
//4、编写代码,演示多个字符从两端移动,向中间汇聚
//welcome效果:
//#######
//w#####e
//we###me
//wel#ome
//welcome
#include <Windows.h>
#include <stdlib.h>
int main() {
char arr1[] = "Welcome to bit!!!!";
char arr2[] = "##################";
int left = 0;
int right = strlen(arr2) - 1;//或者int right=sizeof(arr1) / sizeof(arr1[0])-2 (因为还有\0算一个);//strlen计算的是\0之前的元素个数,再减1就是right的下标
while (left<=right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
Sleep(1000);//sleep单位是ms,要引用头文件Windows.h
//清空屏幕后看起来就像动态打印一样
system("cls");//执行系统命令,system是一个库函数,头文件是stdlib.h
right--;
left++;
}
printf("%s\n", arr2);
return 0;
}
/*效果:
W################!
We##############!!
Wel############!!!
Welc##########!!!!
Welco########t!!!!
Welcom######it!!!!
Welcome####bit!!!!
Welcome ## bit!!!!
Welcome to bit!!!!
*/
//===========================================================
//5、编写代码实现,模拟用户登录情景,并且只能登录三次(只允许输入三次密码,如果密码正确则提示登录成功,如果三次均输入错误,则退出程序。)
//strcmp的头文件是string.h
#include <string.h>
int main() {
int i = 0;
char password[20] = { 0 };
for (i = 0;i < 3;i++)
{
printf("请输入密码:>");
scanf("%s", password);//不用取地址,因为数组名本身就是地址
if (strcmp(password,"abcdef")==0)//比较2个字符串是否相等,不能使用==,而应该使用一个库函数:strcmp,如果返回值是0,表示两个字符串相等
{
printf("login success!\n");
break;
}
else
{
printf("password is incorrect!\n");
}
}
if (i == 3)
{
printf("三次密码均输入错误,退出程序\n");
}
return 0;
}
6、函数调用部分练习(判断素数、闰年、有序数组二分查找、计算调用次数)
//练习部分
// 补充:打印100~200之间的素数
//版本一:
int main() {
//产生100-200之间的数
int i = 0;
int count = 0;
for (i = 100; i <= 200; i++)
{
//判断i是否为素数,是则打印
//素数:只能被1和它本身整除的数(判断7是否为素数:拿2-6之间的数去除)
//拿2~(i-1) 之间的数去试除i
int flag = 1;//flag是1表示是素数
int j = 0;
for (j = 2; j <= i - 1; j++)
{
if (i% j == 0)
{
flag = 0;
break;//是跳过本次循环(if只是判断语句,跳出的是for)
}
}
if (flag == 1) {
count++;
printf("%d ", i);
}
}
printf("\ncount=%d\n", count);
return 0;
}
//版本二(优化)
//思想:m=a*b
//16 = 2*8 = 4*4;a和b中一定有一个数字是<=sqrt(m) 【sqrt的使用要包含头文件math.h】
//eg: sqrt(16)=4 找到了2就不用找8了,没有必要硬生生的把2~i-1的数字都试一遍
//只要试除sqrt(m)之前的数字就可以了(只要在sqrt(i)之前找到一个因子能整除m,说明i就不是素数)【上面的代码只要改一个数】
//再优化:偶数不可能是素数,所以从101开始每次加2
#include <math.h>
int main() {
int i = 0;
int count = 0;
for (i = 101; i <= 200; i+=2)//再改一下这块
{
int flag = 1;
int j = 0;
for (j = 2; j <= sqrt(i); j++)//只要把原来的i-1改成sqrt(i)【sqrt的使用要包含头文件math.h】sqrt(开平方)
{
if (i % j == 0)
{
flag = 0;
break;
}
}
if (flag == 1) {
count++;
printf("%d ", i);
}
}
printf("\ncount=%d\n", count);
return 0;
}
//1、写一个函数可以判断一个数是不是素数;【上面的补充是没有使用函数】
int is_prime(int n)
{
//是素数返回1,不是返回0
int j = 0;
for (j = 2; j <= sqrt(n); j++)//只要把原来的i-1改成sqrt(i)【sqrt的使用要包含头文件math.h】sqrt(开平方)
{
if (n % j == 0)
{
return 0;//不是素数就返回0
//return的功能远远强大于break
//break只会跳出循环,而只要return了,这个函数就结束了,结论就出来了
}
}
return 1;
}
int main()
{
int i = 0;
int count = 0;
for (i = 101; i <= 200; i+=2)
{
if (is_prime(i)) //is_prime可以判断i是不是素数,返回1为真就打印
{
printf("%d ", i);
count++;
}
}
printf("\ncount=%d\n", count);
return 0;
}
//补充:打印1000~2000年之间的闰年
//闰年的判断规则:
//判断规则1:能被4整除,并且不能被100整除的是闰年
//判断规则2:能被400整除
int main()
{
//首先要有1000~2000的数字(for)
int year = 0;
for (year = 1000;year <= 2000;year++)
{
//判断year是不是闰年
if (year % 4 == 0)
{
if (year % 100 != 0)
{
printf("%d ", year);
}
}
if (year % 400 == 0) //不能用else if(否则else if可能不会被执行)
{
printf("%d ", year);
}
}
return 0;
}
//代码简化
int main()
{
//首先要有1000~2000的数字(for)
int year = 0;
for (year = 1000;year <= 2000;year++)
{
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
printf("%d ", year);
}
return 0;
}
//2、写一个函数判断一年是不是闰年;
int is_leap_year(int y)
{
//是闰年返回1,不是返回0
if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
return 1;
else
return 0;
}
int main()
{
int year = 0;
for (year = 1000;year <= 2000;year++)
{
if (is_leap_year(year))
printf("%d ", year);
}
return 0;
}
//3、写一个函数,实现一个整型有序数组的二分查找;
int binary_search(int arr[], int k, int sz)//arr[]是个指针变量,存放首地址,只是长得像数组【形参arr看上去是数组,本质上是指针变量】
//数组传参实际上传递的是数组首元素的地址,而不是整个数组,所以在函数内部计算一个函数参数部分的数组的元素个数是不靠谱的
{
//查找范围的左右下标
int left = 0;//左下标
int right = sz-1;//右下标
while (left<=right)
{
//找到中间下标
int mid = left + (right - left) / 2;//补齐的思想(之前讲过)【为了防止溢出】
if (arr[mid] < k)//说明查找返回在mid的右边
{
left = mid + 1;
}
else if (arr[mid] > k)//说明查找返回在mid的左边
{
right = mid - 1;//右下标改变
}
else
{
return mid;//找到了就返回下标
}
}
return -1;//找不到
}
int main() {
int arr[ ] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 7;
int sz = sizeof(arr) / sizeof(arr[0]);//不能放函数里面,要不然求数组元素个数会是错的
//找到了返回下标,找不到返回-1
int ret=binary_search(arr , k , sz);//去哪查?查谁?元素个数?3个参数【数组传参的时候传的是数组名】//arr是指针变量,存放的是首地址
//写代码习惯:先把函数怎么用写好,再去写这个函数(这个时候这个函数的参数、返回类型就很清晰了)
if (ret == -1)
{
printf("找不到");
}
else
{
printf("找到了,下标是%d\n", ret);
}
return 0;
}
//形式参数和实际参数的名字可以相同也可以不同
//4、写一个函数,每调用一次这个函数,就会将num的值增加1
void Add(int* p)
{
(*p)++; //*p找到num //通过指针找到外部变量去修改
}
int main()
{
int num = 0;
Add(&num);//传地址过去才能改变它
printf("%d\n", num);//1
Add(&num);
printf("%d\n", num);//2
Add(&num);
printf("%d\n", num);//3....
return 0;
}
7、递归与迭代练习(一)
①练习1、接受一个整型值(无符号),按照顺序打印它的每一位。
②练习2、编写函数不允许创建临时变量,求字符串长度
③练习3、求n的阶乘(不考虑溢出)
④练习4、求第n个斐波那契数列(不考虑溢出)
练习1、接受一个整型值(无符号),按照顺序打印它的每一位。
例如:
输入:1234,输出 1 2 3 4.
递归的实现
void print(unsigned int n)
{
if (n > 9)
{
print(n / 10);//
}
printf("%d ", n % 10);//
}
int main()
{
unsigned int num = 0;
scanf("%u", &num);//1234
print(num);//接受一个整型值(无符号),按照顺序打印它的每一位。
return 0;
}
/*
print(1234)
print(123) 4
print(12) 3 4
print(1) 2 3 4
1 2 3 4
%d 是打印有符号的整数(会有正负数)
%u 是打印无符号的整数
*/
练习2、编写函数不允许创建临时变量,求字符串的长度。
求字符串的长度
模拟实现strlen
#include <string.h>
int my_strlen(char str[])//参数部分写出数组的形式
int my_strlen(char* str)//参数部分写出指针的形式
{
int count = 0;//计数,临时变量
while (*str != '\0')
{
count++;
str++;//找下一个字符
}
return count;
}
递归求解
/*
my_strlen("abc");
1+my_strlen("bc");
1+1+my_strlen("c")
1+1+1+my_strlen("")
1+1+1+0
*/
int my_strlen(char* str)
{
if (*str != '\0')
return 1 + my_strlen(str+1);
else
return 0;
}
int main()
{
char arr[] = "abc";//[a b c \0]
//char*
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
练习3、计算n的阶乘(不考虑溢出)
递归实现
int fac(int n)
{
if (n <= 1)
return 1;
else
return n * fac(n - 1);
}
迭代的方式-非递归
int fac(int n)
{
int i = 0;
int ret = 1;
for (i = 1; i <= n; i++)
{
ret *= i;
}
return ret;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = fac(n);
printf("ret = %d\n", ret);
return 0;
}
练习4、求第n个斐波那契数
斐波那契数列
1 1 2 3 5 8 13 21 34 55 ...
//递归实现
int count = 0;
int Fib(int n)
{
if (n == 3)
count++;
if (n <= 2)
return 1;
else
return Fib(n - 1) + Fib(n - 2);
}
//40
//39 38
//38 37 37 36
//37 36 36 35 36 35 35 34
//...
//非递归
int Fib(int n)
{
int a = 1;
int b = 1;
int c = 0;
while (n >= 3)
{
c = a + b;
a = b;
b = c;
n--;
}
return c;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = Fib(n);
printf("%d\n", ret);
//printf("%d\n", count);
return 0;
}
8、写代码将三个整数从大到小输出
输入:2 3 1
输出:3 2 1
int main() {
int a = 0;
int b = 0;
int c = 0;
//输入
scanf("%d %d %d", &a, &b, &c);
//调整(最大的放a,最小的放c)
int tmp = 0;
if (a < b)//a和b交换
{
tmp = a;//杯子倒水一样,找个空杯子
a = b;
b = tmp;
}
if (a < c)//a和c交换
{
tmp = a;
a = c;
c = tmp;
//现在为止最大值已经放在a里面了
}
if (b < c)//现在比较b和c的关系,把小的放c
{
tmp = b;
b = c;
c = tmp;
}
//输出
printf("%d %d %d\n", a, b, c);
return 0;
}
用函数实现:(Swap函数传的是地址,传址调用)
void Swap(int* px, int* py)
{
int tmp = *px;
*px = *py;
*py = tmp;
}
int main()
{
int a = 0;
int b = 0;
int c = 0;
//输入
scanf("%d %d %d", &a, &b, &c);
//调整
if (a < b)
{
Swap(&a, &b);
}
if (a < c)
{
Swap(&a, &c);
}
if (b < c)
{
Swap(&b, &c);
}
//输出
printf("%d %d %d\n", a, b, c);//
return 0;
}
9、打印1-100之间所有3的倍数的数字
//方法1
int main()
{
int i = 0;
for (i = 1;i <= 100;i++) {
if (i % 3 == 0)
printf("%d ", i);
}
return 0;
}
//方法2
int main()
{
int i = 0;
for (i = 3;i <= 100;i+=3) {
printf("%d ", i);
}
return 0;
}
10、给定两个数,求这两个数的最大公约数(辗转相除法)
//给定两个数,求这两个数的最大公约数。如:gcd(18,24)=6
//最大公约数≤min{18,24}=18
方法1、(暴力求解-不高效)
int main()
{
int a = 0;
int b = 0;
//输入
scanf("%d %d", &a, &b);
//求最大公约数
int min = (a < b) ? a : b;
int m = min;
while (1)
{
if (a % m == 0 && b % m == 0)
{
break;//m就是最大公约数,跳出循环
}
m--;//从最小值处每次减1去试
}
printf("%d\n", m);
return 0;
}
方法2、辗转相除法(速度很快)
//a b c
//24 % 18 = 6
//18 % 6 = 0
//故6是最大公约数
//思想:两个数一模,看结果,结果不等于0,再把c赋给b,b赋给a,直到a%b==0,b就是最大公约数
int main()
{
int a = 0;
int b = 0;
int c = 0;
//输入
scanf("%d %d", &a, &b);//18 24和24 18效果是一样的,最终都会挪位变成18 24
//求最大公约数
while (c=a%b)
{
a = b;
b = c;
}
printf("%d ", b);
return 0;
}
11、编写程序数一下1 到 100的所有整数中出现多少个数字9。(20个)
//判断个位是不是9,判断十位是不是9
int main() {
int i = 0;
int count = 0;
for (i = 0;i <= 100;i++)
{
if (i % 10 == 9)//判断个位是不是9
count++;
if (i / 10 == 9)//判断十位是不是9
count++;
}
printf("1 到 100的所有整数中出现%d个数字9 ", count);//20
//else和elseif在一起只会进去一次,只是多分支而已
return 0;
}
12、分数求和
计算1/1-1/2+1/3-1/4+1/5....+1/99-1/100的值,打印出结果。
//计算1 / 1 - 1 / 2 + 1 / 3 - 1 / 4 + 1 / 5.... + 1 / 99 - 1 / 100的值,打印出结果。
//分子都是1
//分母是1~100(可以通过循环产生)
//加减交替
//1/3结果是0不会算出0.3333(要写成1/3.0或者1.0/3,保证有一边有小数)
int main()
{
int i = 0;
double sum = 0;
int flag = 1;
for (i = 1; i <= 100; i++)
{
sum = sum + flag*(1.0 / i);
flag = -flag;//正负交替
}
printf("%lf\n", sum);
return 0;
}
也可以通过分母的奇偶去判断去编程(但是上面写法更简单)
13、求十个整数中的最大值(打擂台:第4题讲过)
int main()
{
//准备十个整数
int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
//找出最大值(打擂台)
int max = arr[0];//假设0号选手最大,再让1-9号选手和它PK
int i = 0;
for (i = 1;i < 10;i++)
{
if (arr[i] > max)
{
max = arr[i];
}
}
printf("%d\n", max);//10
return 0;
}
----------------------
int main()
{
//准备十个整数
int arr[10] = { 0 }; //若没有指定数组元素个数,就会根据初始化的内容来推算元素的个数,所以不能写成int arr[] = { 0 };
//自己输入10个数
int i = 0;
for (i = 0;i < 10;i++)
{
scanf("%d", &arr[i]);
}
//找出最大值(打擂台)
int max = arr[0];//假设0号选手最大,再让1-9号选手和它PK
for (i = 1;i < 10;i++)
{
if (arr[i] > max)
{
max = arr[i];
}
}
printf("%d\n", max);//10
return 0;
}
14、屏幕上打印九九乘法表
//7、屏幕上打印九九乘法表
//1*1=1
//2*1=2 2*2=4
//3*1=3 3*2=6 3*3=9
//...
//行数=项数
int main() {
int i = 0;
//打印9行
for (i = 1;i <= 9;i++)
{
//打印1行
int j = 0;
for (j = 1;j <= i;j++)//几行就有几项
{
printf("%d*%d=%-2d\t", i, j, i * j);//%2d要打印两位整数,1位的话要用空格填充(%-2d的话是两位左对齐,%2d两位右对齐)
}
printf("\n");
}
return 0;
}
/*
1*1=1
2*1=2 2*2=4
3*1=3 3*2=6 3*3=9
4*1=4 4*2=8 4*3=12 4*4=16
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81
*/
功能附加:口诀表的行数和列数自己指定
//功能附加:口诀表的行数和列数自己指定
void print_table(int n)
{
int i = 0;
for (i = 1;i <= n;i++)
{
//打印1行
int j = 0;
for (j = 1;j <= i;j++)//几行就有几项
{
printf("%d*%d=%-2d\t", i, j, i * j);//%2d要打印两位整数,1位的话要用空格填充(%-2d的话是两位左对齐,%2d两位右对齐)
}
printf("\n");
}
}
int main() {
int n = 0; //行数
scanf("%d", &n);
print_table(n);
return 0;
}
15、三子棋游戏
test.c //测试游戏的逻辑
game.c //游戏代码的事项
game.h //游戏代码的声明(函数声明,符号定义)
----------game.h-----------------
#pragma once
#include <stdio.h>
//srand函数的头文件
#include <stdlib.h>
#include <time.h>
#define ROW 3 //要修改很容易(方便扩展棋盘)
#define COL 3
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col);
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋(找没有下棋的位置随机下棋)
void ComputerMove(char board[ROW][COL], int row, int col);
//判断输赢
/*
玩家赢 - '*'
电脑赢 - '#'
平局 - 'Q' (Quit)
游戏继续 - 'C' (Continue)
*/
char IsWin(char board[ROW][COL], int row, int col);
---------game.c-----------
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化棋盘函数实现
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0;i < row;i++)
{
for (j = 0;j < col;j++)
{
board[i][j] = ' ';//初始化
}
}
}
//打印棋盘函数实现
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0;i < row;i++)
{
//打印数据
int j = 0;
for (j = 0;j < col;j++)
{
printf(" %c ", board[i][j]);
if(j<col-1)
printf("|"); //最后一行不打印
}
printf("\n");
//打印分割信息
if (i < row - 1)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if(j<col - 1)
printf("|");
}
printf("\n");
}
}
}
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;//横坐标
int y = 0;//纵坐标
printf("玩家下棋:>\n");
while (1)
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);//范围不能超过棋盘
//坐标范围合法的判断
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (board[x - 1][y - 1] == ' ')//如果要下棋的位置是空格,就可以下棋
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("坐标被占用,请选择其他位置\n");
}
}
else
{
printf("坐标非法,请重新输入!\n");
}
}
}
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋:>\n");
//电脑生成坐标
int x = 0;
int y = 0;
while (1)
{
x = rand() % row;//模3就是0、1、2 【rand生成的是0-32767】
x = rand() % col;//0~2
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
//
// 判断棋盘有没有放满(满了返回1,不满返回0)-就是遍历数组有没有空格
int IsFull(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0;i < row;i++)
{
for (j = 0;j < col;j++)
{
if (board[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
//判断输赢
char IsWin(char board[ROW][COL], int row, int col)
{
//先判断行
int i = 0;
for (i = 0;i < row;i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
{
return board[i][1];
}
}
//判断列
int j = 0;
for (j = 0;j < col;j++)
{
if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')
{
return board[1][j];
}
}
//判断对角线
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
{
return board[1][1];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
{
return board[1][1];
}
//程序走到这,肯定没有人赢,就要判断平局
//没有人赢,棋盘已经放满了的时候就是平局
if (IsFull(board, row, col))
{
return 'Q';
}
//游戏继续
return 'C';
}
----------test.c------------
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//写一部分,测试一部分
void menu()
{
printf("*************************************************\n");
printf("*****************1. play 0. exit****************\n");
printf("*************************************************\n");
}
void game()
{
char ret = 0;
char board[ROW][COL] = { 0 };//棋盘,三行三列(为了扩展棋盘,可以自定义行列)
//初始化棋盘的函数
InitBoard(board, ROW, COL);//棋盘传过去,三行三列,这个函数放在game.h里面去声明,在game.c里面实现
DisplayBoard(board, ROW, COL);//展示棋盘(本质上就是打印数组)
//下棋
while (1)
{
PlayerMove(board, ROW, COL);//玩家下棋
DisplayBoard(board, ROW, COL);//打印棋盘
//判断输赢
ret=IsWin(board, ROW, COL);
if (ret != 'C')
{
break;
}
ComputerMove(board, ROW, COL);//电脑下棋
DisplayBoard(board, ROW, COL);//打印棋盘
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
{
break;
}
DisplayBoard(board, ROW, COL);
}
if (ret == '*')
{
printf("玩家赢\n");
}
else if(ret=='#')
{
printf("电脑赢\n");
}
else
{
printf("平局\n");
}
DisplayBoard(board, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));//设置随机数的生成起点(time函数传NULL返回时间戳,强制类型转换成unsigned)
int input = 0;
do//至少玩一次
{
menu();//打印菜单
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
printf("三子棋\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误!\n");
break;
}
} while (input);//至少做一次
}
16、递归与迭代练习(二)
16-①字符串逆序(递归实现)
题目内容:编写一个函数reverse_string(char * string) (递归实现)
实现:将参数字符串中的字符反向排列,不是逆序打印;
要求:不能使用C函数库中的字符串操作函数。比如char arr[]=“abcdef”; 逆序之后数组内容变成:fedcba
方法1、循环实现
//不用函数实现(用循环实现)
int main() {
char arr[] = "abcdef"; //[a b c d e f \0]
int sz = sizeof(arr) / sizeof(arr[0]); //包括了\0
int left = 0;
int right = sz-2;//因为从0开始,而且sz是包括\0(或者可以用strlen(arr)-1;//strlen不包括\0)
while (left < right)
{
char tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left++;
right--;
}
printf("%s\n", arr);
return 0;
}
----------------------
//用函数实现
void reverse(char arr[])
{
int left = 0;
int right = strlen(arr)-1;//strlen不包括\0)
while (left < right)
{
char tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left++;
right--;
}
}
int main()
{
char arr[] = "abcdef";
reverse(arr);
printf("%s\n", arr);
return 0;
}
方法2、用递归实现:(注意:要把g改成\0,这样bcdef才会看成一个字符串)
//用递归实现(函数自己调用自己)
//abcdefg
//交换a和g,再逆序中间的bcdef
//逆序bcdef又可以化解成交换b和f,逆序cde(拆成了两步)
//思路:把a放到tmp,再把g的位置换成\0,然后从b开始看到\0 逆序一下,然后把a放上去
int my_strlen(char* str)//因为题目中要求不能使用字符串处理的库函数
{
//自己实现strlen函数
int count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;
}
void reverse(char* str)
{
char tmp = *str;//1、首字符放到tmp
//把'g'改成'\0'(这样才能把b向后的字符看成字符串,因为字符串以\0结束)【最后一个元素下标是6】
int len = my_strlen(str);
*str = *(str + len - 1);//2、把最后的g拿到a的位置
*(str + len - 1)='\0';//3、把原来g的位置改成\0
if (my_strlen(str + 1) >= 2)//一个字母的时候不需要逆序 (要写这个判断,不然就是死递归)
{
reverse(str + 1);//4、把b向后的字符串逆序
}
*(str + len - 1) = tmp;//5、把tmp中的a放在\0的位置
}
int main()
{
char arr[] = "abcdef";
reverse(arr);
printf("%s\n", arr);
return 0;
}
方法3、函数有多个参数的实现
//reverse函数有多个参数(不按题目要求,因为题目只给了一个参数)
int my_strlen(char* str)//因为题目中要求不能使用字符串处理的库函数
{
//自己实现strlen函数
int count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;
}
void reverse(char arr[], int left, int right)
{
//1、把left和right各自所对应元素交换一下
char tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
//中间的字符串交换(函数自己调用自己)
if (left < right)
{
reverse(arr, left + 1, right - 1);
}
}
int main()
{
char arr[] = "abcdefg";
int left = 0;
int right = my_strlen(arr) - 1;
reverse(arr, left, right);//把arr数组里left对应元素和right对应元素,left下标和right下标之间的字符串逆序
printf("%s\n", arr);
return 0;
}
16-②、计算一个数的每位之和(递归实现)
写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和。
例如,调用DigitSum(1729),则应该返回1+7+2+9,它的和是19。
int DigitSum(unsigned int n)//1234(每位拆下来求和)
{
//DigitSum(1234)
//DigitSum(123)+4
//DigitSum(12)+3+4
//DigitSum(1)+2+3+4
if (n > 9)
return DigitSum(n / 10) + n % 10;
else
return n;
}
int main()
{
unsigned int n = 0;
scanf("%u", &n);
int sum = DigitSum(n);
printf("%d\n", sum);
}
16-③、递归实现n的k次方(编写一个函数实现n的k次方,使用递归实现)
//递归实现n的k次方(编写一个函数实现n的k次方,使用递归实现)
//pow(n,k) -> n*pow(n,k-1);(n*n^(k-1))
//k=0 1
//k>0 , pow(n,k) -> n*pow(n,k-1);
//k<0, 1.0/(pow(n,-k))
double Pow(int n, int k) //库里面用pow,这里P用大写防止冲突
{
if (k > 0)
return n * Pow(n, k - 1);
else if (k == 0)
return 1;
else
return 1.0 / Pow(n, -k);
}
int main()
{
int n = 0;
int k = 0;
scanf("%d%d", &n, &k);
double ret = Pow(n, k);
printf("%lf\n", ret);
return 0;
}
17、将数组A中的内容和数组B中的内容进行交换。(数组一样大)
//练习1、将数组A中的内容和数组B中的内容进行交换。(数组一样大)
//错误代码
//int main()
//{
// int arr1[] = { 0,1,2,3,4 };
// int arr2[] = { 6,7,8,9,10 };
// int tmp[] = { 0 };
// tmp = arr1;
// arr1 = arr2;
// arr2 = tmp;
// //存在两个问题
// /*
// ①int tmp[] = { 0 }; 这样初始化的时候数组里面只有一个元素;不够供arr1和arr2来交换。(空间肯定不够)
// ②数组名其实是个地址,是一个常量值,不是一块空间,(tmp = arr1;)不能把一个地址赋给一个地址
//
// */
//
// return 0;
//}
//也不能用strcpy,它是用来拷贝字符串的,而这里是整型数组
//正确代码
int main()
{
int arr1[] = { 1,3,5,7,9 };
int arr2[] = { 2,4,6,8,10 };
//用循环实现
int i = 0;
int sz = sizeof(arr1) / sizeof(arr1[0]);
for (i = 0;i < sz;i++)
{
int tmp = arr1[i];
arr1[i] = arr2[i];
arr2[i] = tmp;
}
//数组打印(错误:printf("%d",arr1);)
//对于一个整型数组你要打印它的所有内容,你依然要用循环把每个数打印出来
for (i = 0;i < sz;i++)
{
printf("%d ", arr1[i]);
}
printf("\n");
for (i = 0;i < sz;i++)
{
printf("%d ", arr2[i]);
}
printf("\n");
return 0;
}
18、数组操作
创建一个整型数组,完成对数组的操作
1、实现函数init()初始化数组全为0;
2、实现print()打印数组的每个元素;
3、实现reverse()函数完成数组元素的逆置。
//练习2、数组操作
/*
创建一个整型数组,完成对数组的操作
1、实现函数init()初始化数组全为0;
2、实现print()打印数组的每个元素;
3、实现reverse()函数完成数组元素的逆置。
*/
void Init(int arr[],int sz)
{
int i = 0;
for (i = 0;i < sz;i++)
{
arr[i] = 0;
}
}
void print(int arr[], int sz)
{
int i = 0;
for (i = 0;i < sz;i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void reverse(int arr[], int sz)
{
//知道第一个元素和最后一个元素下标
int left = 0;
int right = sz - 1;
while (left < right)
{
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left++;
right--;
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
//打印数组
print(arr, sz);//1 2 3 4 5 6 7 8 9 0
//数组逆置
reverse(arr, sz); //
print(arr, sz);//逆置后 查看一下 0 9 8 7 6 5 4 3 2 1
//初始化数组
Init(arr, sz);//两个参数
print(arr, sz);//初始化后 查看一下 0 0 0 0 0 0 0 0 0 0
return 0;
}
19、扫雷游戏
20、小乐乐走台阶(BC166-递归函数)【类似于青蛙跳台阶】
/*
描述:小乐乐上课需要走n阶台阶,因为他腿比较长,所以每次可以选择走一阶或者走两阶,那么他一共有多少种走法?
输入描述:输入包含一个整数n (1 ≤ n ≤ 30)
输出描述:输出一个整数,即小乐乐可以走的方法数。
示例:
输入:10
输出:89
*/
//1、小乐乐走台阶(BC166-递归函数)
/*
描述:小乐乐上课需要走n阶台阶,因为他腿比较长,所以每次可以选择走一阶或者走两阶,那么他一共有多少种走法?
输入描述:输入包含一个整数n (1 ≤ n ≤ 30)
输出描述:输出一个整数,即小乐乐可以走的方法数。
示例:
输入:10
输出:89
*/
/*
思路:fib(n) - 计算走n个台阶的走法
当只有一个台阶的时候,只有一种走法
当有2个台阶,2种走法
当有n(n>2)个台阶,?种走法
==>小乐乐第一次走1个台阶,还剩9个(有fib(9)种走法)
==>小乐乐第一次走2个台阶,还剩8个(有fib(8)种走法)
==>如果有十个台阶,fib(10)=fib(9)+fib(8)【小乐乐第一步无非就是1/2个台阶】
==》fib(n)=1 (n=1)
fib(n)=2 (n=2)
fib(n)=fib(n-1)+fib(n-2) (n>2) //这显然就是一个斐波那契数列的问题
*/
int fib(int n)
{
if (n <= 2)
return n;
else
return fib(n - 1) + fib(n - 2);
}
int main()
{
int n = 0;
//input
scanf("%d", &n);
//计算
int m = fib(n);
//output
printf("%d\n", m);
return 0;
}
21、序列中删除指定数字(BC124)
/*
描述:有一个整数序列(可能有重复的整数),现删除指定的某一个整数,输出删除指定数字之后的序列,序列中未被删除数字的前后位置没有发生改变。
数据范围:序列长度和序列中的值都满足 1≤n≤501≤n≤50
输入描述:
第一行输入一个整数(0≤N≤50)。
第二行输入N个整数,输入用空格分隔的N个整数。
第三行输入想要进行删除的一个整数。
输出描述:输出为一行,删除指定数字之后的序列。
//示例:
输入:
6
1 2 3 4 5 9
4
输出:
1 2 3 5 9
注:牛客网支持C99 变长数组的使用
*/
int main()
{
int n = 0;
scanf("%d", &n);
int arr[50];//因为牛客网支持C99,变长数组,也可以写arr[n]
//接收n个数字
int i = 0;
for (i = 0;i < n;i++)
{
scanf("%d", &arr[i]);
}
int del = 0;
//接收删除的值
scanf("%d", &del);
int j = 0;//j作为下标锁定的位置就是用来存放不删除的数据的
for (i = 0;i < n;i++)
{
if (arr[i] != del)//不等于del就把元素存起来【相同就不存了】
{
arr[j++] = arr[i];//存完之后j++(只有放进去的时候j才++)
//相当于arr[j]=arr[i];j++;
}
}
for (i = 0; i < j;i++)
{
printf("%d ", arr[i]);
}
return 0;
}
22、最高分与最低分之差(BC119)
//BC119、最高分与最低分之差
/*
描述:输入n个成绩,换行输出n个成绩中最高分数和最低分数的差。
输入描述:
两行,第一行为n,表示n个成绩,不会大于10000。
第二行为n个成绩(整数表示,范围0~100),以空格隔开。
输出描述:一行,输出n个成绩中最高分数和最低分数的差。
示例:
输入:
10
98 100 99 97 95 99 98 97 96 100
输出:
5
*/
int main()
{
int n = 0;
scanf("%d", &n);
int arr[n];//牛客网支持C99 所以可以写变长数组
int i = 0;
//输入
for (i = 0;i < n;i++)
{
scanf("%d", &arr[i]);
}
//找出最大值
int max = arr[0];//假设最大值就是arr[0] 打擂台
for (i = 1;i < n;i++)
{
if (arr[i] > max)
max = arr[i];
}
//找出最小值
int min = arr[0];//假设最小值就是arr[0] 打擂台
for (i = 1;i < n;i++)
{
if (arr[i] < min)
min = arr[i];
}
printf("%d",max - min);
return 0;
}
-------------------------------------------------------------------------------
//上面的代码可以在很大程度上简化
//找最大值和最小值可以用一个循环
int max = arr[0];//假设最大值就是arr[0] 打擂台
int min = arr[0];//假设最小值就是arr[0] 打擂台
for (i = 1;i < n;i++)
{
if (arr[i] > max)
max = arr[i];
if (arr[i] < min)
min = arr[i];
}
----------------------------------------------------------------------------------
//再简化(获取一个数字就去比较)
int main()
{
int n = 0;
scanf("%d", &n);
int arr[n];//牛客网支持C99 所以可以写变长数组
int i = 0;
int max=0;
int min=100;
//输入
for (i = 0;i < n;i++)
{
scanf("%d", &arr[i]);
if(arr[i]>max)
max = arr[i];
if (arr[i] < min)
min = arr[i];
}
for (i = 1;i < n;i++)
{
if (arr[i] > max)
max = arr[i];
if (arr[i] < min)
min = arr[i];
}
printf("%d",max - min);
return 0;
}
23、字母大小写转换(BC15)
//BC15、字母大小写转换
/*
描述:实现字母的大小写转换。多组输入输出。
输入描述:多组输入,每一行输入大写字母。
输出描述:针对每组输入输出对应的小写字母。
示例
输入:
A
B
输出:
a
b
备注:多组输入过程中要注意“回车”也是字母,所以要“吸收”(getchar())掉该字母。
*/
int main()
{
char ch=0;
//scanf函数读取成功一个字符会返回1(读到完整的一个数据),如果不能再读 读取停止/失败会返回EOF
//这个时候可以通过判断返回值是不是EOF来判断它是否结束
//或者说读取成功返回1,那么返回的不是1就是返回失败
while (scanf("%c", &ch) == 1)//scanf返回值是1说明它确实读取到了一个字符
{
if (ch >= 'a' && ch <= 'z')
printf("%c\n", ch - 32);
//输入的不是大写就是小写(题目说了输入字母)
else
printf("%c\n", ch + 32);
getchar();//用于销毁\n
}
return 0;
}
//也可以不销毁\n,直接就不判断(用else if,只判断字母)
/*
while (scanf("%c", &ch) == 1)//或者写while (scanf("%c", &ch) != EOF)
{
if (ch >= 'a' && ch <= 'z')
printf("%c\n", ch - 32);
//输入的不是大写就是小写(题目说了输入字母)
else if (ch >= 'A' && ch <= 'Z')
printf("%c\n", ch + 32);
}
return 0;
}
*/
=====================================================================
scanf读取成功的时候,返回的是读取数据的个数
scanf在读取失败的时候返回EOF
还有库函数islower用于判断是不是小写字母 isupper用于判断是不是大写
小写转大写用toupper(ch) 大写转小写用tolower(ch)
这些函数使用的头文件是<ctype.h>
24、判断是不是字母(有个小tip)
//BC60 判断是不是字母
/*
描述:KiKi想判断输入的字符是不是字母,请帮他编程实现。
输入描述:多组输入,每一行输入一个字符。
输出描述:针对每组输入,输出单独占一行,判断输入字符是否为字母,输出内容详见输出样例。
示例1
输入:
A
6
输出:
A is an alphabet.
6 is not an alphabet.
*/
int main()
{
char ch = 0;
while (scanf("%c", &ch) == 1)//读取正常
{
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
printf("%c is an alphabet.\n",ch);
else
printf("%c is not an alphabet.\n",ch);
getchar();//把\n销毁
}
return 0;
}
//还有一种不写getchar的方法:
while (scanf(" %c", &ch) == 1)//在%c的前面加空格:就是跳过下一个字符之前的所有空白字符(比较小众的写法)
判断是不是字母还有一个库函数isalpha
25、最高分数(用边输入变判断的写法去写)【类似于22题】
//最高分数
/*
输入:94 98 99
输出:99
*/
int main()
{
int i = 0;
int max = 0;
int score = 0;
for (i = 0;i < 3;i++)
{
scanf("%d", &score);
if (score > max)
max = score;
}
printf("%d\n", max);
return 0;
}
26、变种水仙花数(BC92)
//变种水仙花数
/*
描述:
变种水仙花数 - Lily Number:把任意的数字,从中间拆分成两个数字,比如1461 可以拆分成(1和461),(14和61),(146和1),如果所有拆分后的乘积之和等于自身,则是一个Lily Number。
例如:
655 = 6 * 55 + 65 * 5
1461 = 1*461 + 14*61 + 146*1
求出 5位数中的所有 Lily Number。
输入描述:无
输出描述:一行,5位数中的所有 Lily Number,每两个数之间间隔一个空格。
*/
#include <math.h>
int main()
{
//产生五位数
int i = 0;
for (i = 10000;i <= 99999 ;i++)
{
//判断i是否为Lily Number
int sum = 0;
int j = 0;
for (j = 1; j <= 4; j++)
{
int k = (int)pow(10, j);
sum += (i % k) * (i / k);
}
if (sum == i)
printf("%d ", i);
}
return 0;
}
额外补充:getchar输入数据存在的问题
补充:用%c输入数据存在的问题
解决方法:
空白字符包括:Tab键、回车符、空格
用简单变量作为函数参数的时候,实参和形参是占用不同的存储单元的
而用数组作为函数的参数的时候,传过去的是首地址,形参数组和实参数组是占同一段存储单元的。