1.猜名次
1°题目内容
5位运动员参加了10米台跳水比赛,有人让他们预测比赛结果:
A选手说:B第二,我第三;
B选手说:我第二,E第四;
C选手说:我第一,D第二;
D选手说:C最后,我第三;
E选手说:我第四,A第一;
比赛结束后,每位选手都说对了一半,请编程确定比赛的名次。
2°思路
真话为1 假话为0 相加来作为if的判断条件
3°实现
#include<stdio.h>
//真话为1 假话为0 相加来作为if的判断条件
int main()
{
int a=0;
int b=0;
int c=0;
int d=0;
int e=0;
for(a=1;a<=5;a++)
{
for(b=1;b<=5;b++)
{
for(c=1;c<=5;c++)
{
for(d=1;d<=5;d++)
{
for(e=1;e<=5;e++)
{
//表达式为真就得到1
//表达式为假就得到0
//真话一半 假话一半
//和为1
//5个人都是一半真一半假
if(((b==2)+(a==3)==1)&&
((b==2)+(e==4)==1)&&
((c==1)+(d==2)==1)&&
((c==5)+(d==3)==1)&&
((e==4)+(a==1)==1))
{
//不论谁是1 2 3 4 5
//1*2*3*4*5=120
if(a*b*c*d*e==120)
printf("a=%d b=%d c=%d d=%d e=%d\n",a,b,c,d,e);
}
}
}
}
}
}
return 0;
}
关键在于利用表达式为真 整个表达式的值就为1 为假 值为0
都从1到5遍历一遍 最后相乘得到120就打印
4°运行结果
2.猜凶手
1°题目内容
日本某地发生了一件谋杀案,警察通过排查确定杀人凶手必为4个嫌疑犯的一个。
以下为4个嫌疑犯的供词:
A说:不是我。
B说:是C。
C说:是D。
D说:C在胡说
已知3个人说了真话,1个人说的是假话。
现在请根据这些信息,写一个程序来确定到底谁是凶手。
2°思路
真话看成表达式为真 值为1 假 值为0
遍历A到D
3°实现
#include<stdio.h>
int main()
{
int killer=0;
for(killer='a';killer<='d';killer++)
{
if((killer!='a')+(killer=='c')+(killer=='d')+(killer!='d')==3)
{
printf("killer=%c\n",killer);
}
}
return 0;
}
用字符a到d 转化成ASCII值是连续的 可遍历
四句话分别对应if里的条件表达式
4°运行结果
扩展:
赛马问题:
36匹马 没有计时器 共6个跑道 最少赛几次 可以知道36匹马中的前三名?
1:先比较6次 这样就有6组的名次
2:接下来把每组的第一名组成一组比1次(第7次)
这组比完后 第一名就是36匹马的第一名
3:
(第7次)第一名的第一次比较的一组中的第二名和第三名有可能是第二和第三(三匹最快的马在同一组)
(第7次)第二名的第一次比较的一组中的自己可能是36匹马中的第二名 第二名可能是36匹马的第三名(一匹最快的马在一组 两匹最快的马在另一组)
(第7次)第三名的第一次比较的一组中只有自己才有可能是36匹马的第三名 这一组后面的马都不可能成为第三了
4:也就是说第一名的那一组的前三匹马和第二名的那一组的前两匹马和第三名的那一组的前一匹马进行比较 这一次(第8次)的比较的前三名就是36匹马中的前三名
假设(第7次比较)第一组的第一匹马是第一 第二组的第一匹马是第二 第三组的第一匹马是第三
框中6匹马进行比较 其他马不可能排第二和第三了
所以最后比较次数是8次
如果25匹马 5个跑道?
同理
先比5次 再把第一都拿出来比较1次
第一的那一组的第二第三可能是第二第三
第二的那一组的第一第二可能是第二第三
第三的那一组的第一可能是第三
这里有6匹马
把已经得出来的第一名去掉
剩下5匹马再比1次
这次的第一就是25匹马的第二 第二就是25匹马的第三
烧香问题:
有一种香 材质不均匀 但是每一根这样的香水 烧完恰好1小时
2根香 确定15min时间?
第一根香两头点燃 第二根香燃一头
第一根烧完后再点燃第二根香的另一头
3.从大到小输出数字
1°题目内容
定义3个整型变量
输入这3个整型变量
最后打印出的结果是按顺序从大到小的三个数字
2°思路
定义a b c
让a成为最大值 b次之 c为最小值
进行三次判断if(a<b) if(a<c) if(b<c)
并进行交换
让a是最大值 b比c大 最后打印按a b c的顺序打印就可以了
3°实现
#include<stdio.h>
int main()
{
int a=0;
int b=0;
int c=0;
scanf("%d%d%d",&a,&b,&c);//2 1 3
//a中放最大值
//b次之
//c中放最小值
if(a<b)
{
int tmp=a;
a=b;
b=tmp;
}
if(a<c)
{
int tmp=a;
a=c;
c=tmp;
}
if(b<c)
{
int tmp=c;
c=b;
b=tmp;
}
printf("%d %d %d",a,b,c);
return 0;
}
4°运行结果
4.按顺序(从高位到低位)打印一个数的每一位数
1°题目内容
输入一个数
从高位到低位输出每一位数
2°思路
假设这个数是n
n/1%10就可以拿到个位
n/10%10就可以拿到十位
n/100%10就可以拿到百位
推下去可以拿到每一位 再进行打印
关键是从高位从低位打印
如果n>9的话就一直除以10 这样就可以拿到最高位
最后再用递归实现
3°实现
#include<stdio.h>
void print(int n)
{
if (n > 9)
{
print(n / 10);//调用完后 执行完会回来 一直打印 1 2 3 4
}
printf("%d ", n % 10);
}
int main()
{
unsigned int num = 0;
scanf("%d", &num);
print(num);
//1234
//123 4
//12 3 4
//1 2 3 4拆开
return 0;
}
4°运行结果
5.用递归实现n的阶乘
1°题目内容
输入一个数
输出这个数的阶乘
用递归实现
2°思路
函数中:
当n==1的时候就直接返回1
当n>1的时候 要把阶乘值返回 返回n*函数(n-1) 此时n-1为判断条件
3°实现
#include<stdio.h>
int Fac2(int n)
{
if (n <= 1)//n=1时 1的阶乘为1 直接返回1
return 1;
else
return n * Fac2(n - 1);//Fac(n-1)会再走一遍函数 这时候n-1为判断条件
}
int main()
{
int n = 0;
int ret = 0;
scanf("%d", &n);
ret = Fac2(n);
printf("%d\n", ret);
return 0;
}
4°运行结果
6.递归/不递归求字符串长度
1°题目内容
输入字符串
输出字符串的长度
用递归/不递归实现
2°思路
不递归:
\0是结束标志
如果不是\0遍历整个字符串
然后计数 最后返回
返回后打印
递归:
不是\0就返回1+函数(下一个字符)
通过返回值来计数
3°实现
//不递归:
#include<stdio.h>
#include<string.h>
int my_strlen(char* str)
{
int count = 0;
while (*str != '\0')
{
count++;
str++;//字符串 往下跳 count计数
}
return count;
}
int main()
{
char arr[1000];
gets(arr);
int len = my_strlen(arr);//arr是数组 数组传参 传过去的是第一个元素的地址
printf("len=%d\n", len);
return 0;
}
//递归:
#include<stdio.h>
#include<string.h>
int my_strlen(char* str)
{
if (*str != '\0')
return 1 + my_strlen(str + 1);
else
return 0;
}
//my_strlen("bit")
//1+my_strlen("it")
//继续
int main()
{
char arr[1000];
gets(arr);
int len = my_strlen(arr);//arr是数组 数组传参 传过去的是第一个元素的地址
printf("len=%d\n", len);
return 0;
}
4°运行结果
非递归:
递归:
7.二分查找
1°题目内容
输入一个数为数组元素的个数
输入一个有序的数组
输入一个数进行查找
输出相关信息
如果找到了 输出该数的下标
如果没有找到 输出找不到的提示
2°思路
left为最左边的下标
right为最右边的下标
mid为中间下标
访问mid下标元素
看该元素与所找元素进行比较
从而改变左下标和右下标来缩小查找范围
3°实现
#include<stdio.h>
int binary_search(int arr[], int k, int sz)
{
//算法的实现
//int sz=sizeof(arr)/sizeof(arr[0]);//数组传参传过去 不能这样求
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = (left + right) / 2;//写里面 每回循环完都要重新算一遍
if (arr[mid] < k)
{
left = mid + 1;
}
else if (arr[mid] > k)
{
right = mid - 1;
}
else
{
return mid;
}
}
return -1;
}
int main()
{
//二分查找
//在一个有序数组中查找具体的某个数
//如果找到了返回下标 找不到的返回-1
int arr[100];
int sz = sizeof(arr) / sizeof(arr[0]);
int s = 0;
int i = 0;
scanf("%d", &s);
for (i = 0; i < s; i++)
{
scanf("%d", &arr[i]);
}
int k = 0;
scanf("%d", &k);
//传递过去的是数组arr首元素的地址 本质上arr是一个指针
int ret = binary_search(arr, k, s);
if (ret == -1)
{
printf("找不到指定的数字\n");
}
else
{
printf("找得到,下标是:%d\n", ret);
}
return 0;
}
4°运行结果
8.斐波那契数列(非递归)
1°题目内容
输入1个数为斐波那契数列的项数
输出这个项数对应的数
2°思路
第三个数等于第一个数和第二个数相加
接下来把第二个数变为第一个数 第三个数变为第二个数
重复相加过程
把b赋给a 把c赋给b 再次a+b 相当于变成后第二个数加第三个数得到第四个数
循环条件当n>2时开始循环 n=3的时候要算1次 n=4要算两次 所以每次算完后n--
3°实现
#include<stdio.h>
long long int Fib(long long int n)
{
long long int a=1;//n<=2时 返回1
long long int b=1;
long long int c=1;
while(n>2)//循环实现
{
c=a+b;
a=b;
b=c;
n--;//算的次数
}
return c;
}
int main()
{
long long int n=0;//定义n 求第n个
long long int ret=0;//ret 最后结果
scanf("%lld",&n);//输入n
ret=Fib(n);//ret=函数()最后结果
printf("ret=%lld\n",ret);
return 0;
}
4°运行结果
9.分数求和
1°题目内容
计算1/1-1/2+1/3-1/4……………………+1/99-1/100的和
2°思路
都是加号就直接循环累加就行了 符号改变了不好办
可以定义一个int flag=1
循环走一次就变一次号
3°实现
#include<stdio.h>
int main()
{
int i = 0;
double sum = 0.0;
int flag = 1;
for (i = 1; i <= 100; i++)
{
sum += flag * 1.0 / i;//直接累加效果 分数求和
flag = -flag;//加减交替求和 巧妙换号
}
printf("sum=%lf\n", sum);
return 0;
}
4°运行结果
10.喝汽水问题
1°题目内容
喝汽水 一瓶汽水1元 两个空瓶子能换一瓶汽水 问n元钱能喝多少汽水
输入钱数
输出可以喝的瓶数
2°思路
假设20块
可以看成一个空瓶0.5元 20元就是40个空瓶 也就能喝40瓶 但最后肯定会留一个空瓶
所以喝的总瓶数为39瓶 也就时喝的总瓶数就是2*钱数-1(规律)
没有规律怎么写?
换汽水后
只有空瓶大于两个
总数就要加上空瓶数
空瓶变成换的汽水瓶数(喝完了就是空瓶)加上没有换的空瓶
3°实现
#include<stdio.h>
int main()
{
int money=0;//规律total=2*money-1 一个空瓶0.5元 20块等于40个空瓶 最后你会留一瓶空瓶 -1=39
int total=0;//喝掉总数量
int empty=0;//空瓶数量
scanf("%d",&money);
//买回的汽水喝掉
total=money;
empty=money;
//换回来的汽水
while(empty>=2)
{
total+=empty/2;
empty=empty/2+empty%2;//换的汽水瓶子加剩下空瓶
}
printf("%d",total);
return 0;
}
4°运行结果
11.逆序字符串
1°题目内容
输入一个字符串
输出这个的字符串的逆序字符串
比如说输入str 输出rts
2°思路
比如student
实际上是从第一个和最后一个开始交换
然后第二个和倒数第二个交换
直到走完整个字符串
要改变字符串 就要拿到对应字符的指针
第一个字符好拿
最后一个字符就是第一个字符加上字符串长度-1
在一个字符串中字符的地址是连续的
可以通过加减得到地址
3°实现
#include<stdio.h>
#include<string.h>
//#include<assert.h>
void reverse(char* str)
{
//assert(str);
int len = strlen(str);
char* left = str;//首元素地址
char* right = str + len - 1;//最后一个元素地址 起始地址加长度减一
while (left <= right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
int main()
{
char arr[256] = { 0 };
scanf("%s", &arr);
reverse(arr);
printf("%s\n", arr);
return 0;
}
4°运行结果
12.杨氏矩阵
1°题目内容
杨氏矩阵
有一个二维数组 数组的每行从左到右是递增的 每列从上到下是递增的
在杨氏矩阵中找到相应的元素(要求:不能遍历)
输入查找的数
输出
如果找到了 输出下标
如果没找到 提示没找到
2°思路
这个数组的右上角的数很重要
只要这个数比右上角的数小
那么表示这一列没有这个数
只要这个数比右上角的数大
那么表示这一行没有这个数
每次要重置右上角的数 改变下标
3°实现
#include<stdio.h>
int FindNum(int arr[3][3], int k, int* px, int* py)
{
int x = 0;
int y = *py - 1;
while (x <= *px - 1 && y >= 0)//在范围内多次查找
{
if (arr[x][y] > k)
{
y--;
}
else if (arr[x][y] < k)
{
x++;
}
else
{
*px = x;//直接返回到外面的x y 此时找到了
*py = y;
return 1;
}
}
return 0;
}
int main()
{
int arr[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
int k = 0;
scanf("%d", &k);
int x = 3;
int y = 3;
//先找到右上角的下标 通过右上角的下标开始扫描整个数组
int ret = FindNum(arr, k, &x, &y);//传x y地址 返回型参数
if (ret == 1)
{
printf("找到了\n");
printf("下标是%d %d\n", x, y);
}
else
{
printf("找不到\n");
}
return 0;
}
4°运行结果
13.字符串左旋
1°题目内容
实现一个函数,可以左旋字符串中的k个字符。
例如:
ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB
2°思路
暴力:
把第一个字符存起来
把后面的字符往前挪一格
再把存起来赋到最后一个字符
要实现多次左旋就加上循环
三步旋转法:
利用逆序字符串函数:
将首字符到第k个字符逆序
再将第k+1个字符到最后一个字符逆序
最后将整体逆序
3°实现
暴力:
#include <stdio.h>
void left_move(char arr[], int k)//第一个参数写char* arr也可
{
int i = 0;
int len = strlen(arr);
for (i = 0; i < k; i++)
{
//左旋一个字符
//挪
char tmp = *arr;//第一个元素
int j = 0;
for (j = 0; j < len - 1; j++)//j+1<len
{
*(arr + j) = *(arr + j + 1);//看成arr[j]=arr[j+1]
}
//最后一个元素
*(arr + len - 1) = tmp;
}
}
int main()
{
char arr[100];
gets(arr);
int k = 0;
scanf("%d", &k);
left_move(arr, k);
printf("%s\n", arr);
return 0;
}
三步旋转法:
//2三步翻转法
//abcdef
//bafedc
//cdefab
#include<stdio.h>
#include<string.h>
#include<assert.h>
reverse(char* left, char* right)//逆序的实现 左边下标与对应的右边下标交换
{
while (left < right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
void left_move(char* arr, int k)
{
assert(arr);
int len = strlen(arr);
assert(k <= len);
reverse(arr, arr + k - 1);//逆序左边
reverse(arr + k, arr + len - 1);//逆序右边
reverse(arr, arr + len - 1);//逆序整体
}
int main()
{
char arr[100];
gets(arr);
int k = 0;
scanf("%d", &k);
left_move(arr, k);
printf("%s\n", arr);
return 0;
}
4°运行结果
暴力:
三步旋转法:
14.奇数偶数交换
1°题目内容
一个数组中的偶数和奇数进行交换
1 2 3 4 5 6 7 8 9 10
1 9 3 7 5 6 4 8 2 10
2和9 4和7交换
3和8 5和6不交换
2°思路
这种情况的交换是左边有偶数就换 右边有奇数就换
从左边开始找偶数 右边开始找奇数
找到了就换
找不到往下走
3°实现
#include<stdio.h>
//从左边开始找一个偶数
//从右边开始找一个奇数
//交换
void move(int arr[],int sz)
{
int left=0;
int right=sz-1;
while(left<right)
{
//从左边找偶数 如果全是奇数 无left<right会越界
while((left<right)&&arr[left]%2==1)
{
left++;
}
//从右边找奇数
while((left<right)&&arr[right]%2==0)
{
right--;
}
//交换
if(left<right)
{
int tmp=arr[left];
arr[left]=arr[right];
arr[right]=tmp;
}
}
}
void print(int arr[],int sz)
{
int i=0;
for(i=0;i<sz;i++)
{
printf("%d ",arr[i]);
}
}
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9,10};
int sz=sizeof(arr)/sizeof(arr[0]);
move(arr,sz);
print(arr,sz);
return 0;
}
4°运行结果
15.判断字符旋转
1°题目内容
写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。
例如:给定s1 =AABCD和s2 = BCDAA,返回1
给定s1=abcd和s2=ACBD,返回0.
AABCD左旋一个字符得到ABCDA
AABCD左旋两个字符得到BCDAA
AABCD右旋一个字符得到DAABC
2°思路
1:
左旋字符串加判断是否相等 旋转1次就比较1次 直到旋转回到原字符串
2:
在s1字符串后追加一个s1 再判断s1中的子串有没有s2
NOOB就是RUNOOB的子串 当一个字符串重复两次后就包含了所有的旋转情况
只要再判断是不是子串就可以了
找子串用strstr函数
自己追加自己不能用strcat
只能用strncat
详细函数介绍与自定义函数实现可见:
3°实现
1:
#include<stdio.h>
#include<string.h>
void left_move(char arr[],int k)//第一个参数写char* arr也可
{
int i=0;
int len=strlen(arr);
for(i=0;i<k;i++)
{
//左旋一个字符
//挪
char tmp=*arr;//第一个元素
int j=0;
for(j=0;j<len-1;j++)//j+1<len
{
*(arr+j)=*(arr+j+1);//看成arr[j]=arr[j+1]
}
//最后一个元素
*(arr+len-1)=tmp;
}
}
int is_left_move(char s1[],char s2[])
{
int len=strlen(s1);
int i=0;
for(i=0;i<len;i++)
{
left_move(s1,1);//旋转1个字符比较1次
int ret=strcmp(s1,s2);
if(ret==0)
return 1;
}
return 0;
}
int main()
{
char arr1[100];//写成指针的话 是常量字符串 改变不了
char arr2[100];
gets(arr1);
gets(arr2);
int ret=is_left_move(arr1,arr2);
if(ret==1)
printf("Yes\n");
else
printf("No\n");
return 0;
}
2:
#include <stdio.h>
#include <string.h>
int is_left_move(char* str1, char* str2)
{
int len1 = strlen(str1);
int len2 = strlen(str2);
if (len1 != len2)
return 0;//字符长度一样 不一样可能输出yes
//1在str1后面追加一个str1 stract自己给自己加会出问题
//strcat(s1,s1);error
strncat(str1, str1, 6);//最后一个参数为添加字符个数
//2判断str2指向的字符串是否是str1指向的字符串的子串
//strstr-找子串
char* ret = strstr(str1, str2);
if (ret == NULL)
{
return 0;
}
else
{
return 1;
}
}
int main()
{
char arr1[100];
char arr2[100];
gets(arr1);
gets(arr2);
int ret = is_left_move(arr1, arr2);
if (ret == 1)
{
printf("Yes\n");
}
else
{
printf("No\n");
}
return 0;
}
4°运行结果
1:
2:
#17C语言练习题#完