第一题(附加)
声明一个指向含有10个元素的数组的指针,其中每个元素是一个函数指针,该函数的返回值是int,参数是int*,正确的是( )
A.(int *p[10])(int*)
B.int [10]*p(int *)
C.int (*(*p)[10])(int *)
D.int ((int *)[10])*p
评析:本题作为阿里巴巴的一道笔试题,它的解答在网上众说纷纭,几乎没有说到点上。
解析:根据题意可知,声明一个指向数组的指针,所以这个指针肯定是数组指针之类的
数组的十个元素都是函数指针,函数指针所指向的函数返回值都是int,参数都是int*。读到这里
我们想一想,有什么数组居然可以装变量,装函数指针,显然没有这样的数组,数组内装的都是数值,所以这个数组装的是函数指针所指向的函数的地址,所以这个数组是什么?是一级函数指针数组, 它指向这十个函数的地址,题目要求就转变为声明一个指向一级函数指针数组的指针,那就是
二级函数数组指针,根据原函数返回值都是int,参数都是int*,我们可以直接写出答案
int (*(*p)[10]) (int *)
第二题
打印9*9乘法口诀表
#include <stdio.h>
int main()
{
int i = 0;
int j = 0;
for (i = 1; i <= 9; i++)
{
for (j = 1; j <= i; j++)
{
printf("%d*%d=%2d ", i, j, i * j);
}
printf("\n");
}
return 0;
}
第三题
打印100~200之间的素数
#include <stdio.h>
#include <math.h>
int main()
{
int i = 0;
for (i = 101; i <= 200; i+=2)//因为偶数不是素数,直接跳过
{
int j = 0;
int flag = 1;//定义变量记录当前数是否为素数,假设是素数,变量值为1
for (j = 2; j <=sqrt(i); j++) //遍历2~当前数的根号
{
if (i % j == 0)//存在⼀个数可以整除当前数
{
flag = 0;//不是素数
break;
}
}
if (flag == 1)//是素数就打印
printf("%d ", i);
}
return 0;
}
解析:这是本题最优算法的解答,sqrt(i)是对i的开根号,关于j为什么就取到√i,我在这里稍作解释,√i * √i =i,如果i是合数,就一定至少存在一对除1和它本身的正因数,这两个正因数其中有一个必然小于或等于√i,另一个必然大于或等于√i。只要找到第一个正因数就直接判定是合数。
第四题
输⼊三个整数a,b,c,判断由a,b,c作为三条边组成的三角形,如果不能组成三角形则输出:非三角形;如果是三角形,再继续判断,如果是等边三角形,则输出:等边三角形;如果是等腰三角形,则输出:等腰三角形;否则输出普通三角形。
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
int c = 0;
scanf("%d %d %d", &a, &b, &c);
if (a + b > c && a + c > b && b + c > a)
{
if (a == b && b == c)
{
printf("等边三⻆形\n");
}
else if (a == b || a == c || b == c)
{
printf("等腰三⻆形\n");
}
else
{
printf("普通三⻆形\n");
}
}
else
{
printf("⾮三⻆形\n");
}
return 0;
}
第五题
输⼊2个整数m和n,计算m和n的最大公约数
#include <stdio.h>
int main()
{
int m = 0;
int n = 0;
scanf("%d %d", &m, &n);
//辗转相除法
int k = 0;
while (k = m % n)//当n不能整除m,即k≠0,更新两个最值重复步骤计算n与m%n的最⼤公约数
{
m = n;
n = k;
}
printf("%d\n", n);
return 0;
}
第六题
输入2个整数m和n,计算m和n的最小公倍数
#include <stdio.h>
int main()
{
int m = 0;
int n = 0;
//输⼊
scanf("%d %d", &m, &n);//18 24
int k = 0;
int mul = m*n;
while (k = m % n)//辗转相除法求得最⼤公约数
{
m = n;
n = k;
}
printf("%d\n", mul/n);
return 0;
}
解析:最小公倍数可以由两数乘积除以两数的最大公约数求得
第七题
计算 1/1 - 1/2 + 1/3 - 1/4 + 1/5 - … + 1/99 - 1/100 的值
#include<stdio.h>
int main()
{
float i,sum=0;
for(i=1;i<=100;i+=2)
{
sum+=1/i-1/(i+1);
}
printf("%f",sum);
return 0;
}
第八题
输入10个整数,使用冒泡排序对数组内容进行升序排序
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
//输⼊数据
int i = 0;
for (i = 0; i < sz; i++)
{
scanf("%d", &arr[i]);
}
//冒泡排序
for (i = 0; i < sz-1; i++)
{
int j = 0;
int flag = 1;//假设待排序的数据已经有序
//对未排序元素进⾏冒泡排序
for(j=0; j < sz-1-i; j++)
{
//当前相邻元素顺序错误,进⾏交换
if (arr[j] > arr[j + 1])
{
flag = 0;//只要交换说明正在排序的数据不是有序的
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
//当前序列已经是升序状态,结束循环
if (flag == 1)
break;
}
//输出
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
第九题
某地发生了一件盗窃案,警察通过排查确定盗窃者必为4个嫌疑人的一个。以下为4个嫌疑人的供词:A说:不是我。B说:是C。C说:是D。D说:C在胡说已知3个人说了真话,1个人说的是假话。现在请根据这些信息,写⼀个程序来确定到底谁是盗窃者。
#include<stdio.h>
int main()
{
int thieves = 0;
//分别假设盗窃者是a,b,c,d,看谁是盗窃者时满⾜3个⼈说了真话,⼀个⼈说了假话
for (thieves = 'A'; thieves <= 'D'; thieves++)
{
//判断当前嫌疑⼈作为盗窃者是否成⽴
if ((thieves != 'A') + (thieves == 'C') + (thieves == 'D') + (thieves !='D')==3)
printf("盗窃者是:%c", thieves);
}
return 0;
}
解析:本题题型新颖,利用字符串和条件筛选即可写出程序
第十题
写⼀个代码打印1~100000之间的所有的自幂数
#include <stdio.h>
#include <math.h>
int main()
{
int i = 0;
for (i = 1; i <= 100000; i++)
{
//判断i是否是⾃幂数
//1. 计算i的位数n
int n = 1;
int tmp = i;
while (tmp/10)
{
n++;
tmp /= 10;
}
//2. 计算i的每⼀位的n次⽅之和
tmp = i;
int sum = 0;
while (tmp)
{
sum += pow(tmp % 10, n);
tmp /= 10;
}
//3. 输出
if (sum == i)
printf("%d ", i);
}
return 0;
}
解析:一个n位自然数等于自身各个数位上数字的n次幂之和,则称此数为自幂数
第十一题
输入一个整数n,打印对应2*n-1行的菱形图案比如,输入7,输出如下图案,图案总共13行
#include <stdio.h>
int main()
{
int n = 0;
//输⼊
scanf("%d", &n);
//打印
//打印上半部分的n⾏
int i = 0;
for (i = 0; i < n; i++)
{
//打印空格
int j = 0;
for (j = 0; j < n-1-i; j++)
{
printf(" ");
}
//打印*
for (j = 0; j < 2*i+1; j++)
{
printf("*");
}
printf("\n");
}
//打印下半部分的n-1⾏
for (i = 0; i < n; i++)
{
//打印空格
int j = 0;
for (j = 0; j <=i; j++)
{
printf(" ");
}
//打印*
for (j = 0; j < 2*(n-1-i)-1; j++)
{
printf("*");
}
printf("\n");
}
return 0;
}
解析:根据n找出各行空格和*数量的规律
第十二题
汽水已知1瓶汽水1元,2个空瓶可以换一瓶汽水,输入整数n(n>=0),表示n元钱,计算可以喝多少瓶汽水
#include <stdio.h>
int main()
{
int n = 0;
int total = 0;//表⽰总共能喝多少汽⽔
int empty = 0;//表⽰⼿⾥的空瓶数
scanf("%d", &n);
//买n瓶汽⽔,并且喝完后剩余n个空瓶
total += n;
empty += n;
//重复⽤空瓶⼦购买汽⽔直⾄空瓶不够2个
while (empty >= 2)
{
total += empty / 2;
empty = empty / 2 + empty % 2;
}
printf("%d\n", total);
return 0;
}
解析: 第一次循环
total += empty / 2 计算了 第一次喝的n瓶+第二次喝的n/2瓶
empty = empty / 2 + empty % 2 计算第三次喝的瓶数就需要计算第二次剩余的瓶数
第二次循环
total += empty / 2 计算了 第一次喝的n瓶+第二次喝的n/2瓶 + 第三次喝的瓶数
empty = empty / 2 + empty % 2 计算第四次喝的瓶数就需要计算第三次剩余的瓶数
..........
直到剩余的空瓶数小于2,也就是1和0,换不了汽水了,循环结束
第十三题
写⼀个函数Swap,可以交换两个整数的内容。
#include <stdio.h>
//传⼊两个整型指针参数,进⾏交换
void Swap(int* pa, int* pb)
{
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
int main()
{
int a = 0;
int b = 0;
//输⼊
scanf("%d %d", &a, &b);
//将需要交换值的两个整数变量的地址作为参数传递给函数,进⾏交换
Swap(&a, &b);
//输出
printf("%d %d\n", a, b);
return 0;
}
解析:由于函数只能返回一个值,所以无法使用return实现两个数的交换
除了利用指针进行交换,这里还可以定义两个全局变量进行交换,也可以使用数组
因为数组的本质也是指针
第十四题
输入2个整数m和n,写⼀个函数average,求2个整数的平均值,考虑整数过大溢出的问题
#include <stdio.h>
//⽅法1:这种⽅式数字太⼤是会溢出
int average(int x, int y)
{
return (x + y) / 2;
}
//⽅法2:改进
int average(int x, int y)
{
return x + (y-x) / 2;
}
int main()
{
int m = 0;
int n = 0;
//输⼊
scanf("%d %d", &m, &n);
//求平均值
int avg = average(m, n);
//输出
printf("%d\n", avg);
return 0;
}
解析:本题主要强调了求平均的保险求法 x + (y-x) / 2,这样就不会溢出,从而防止截断错误
第十五题
写⼀个函数Strlen,可以求字符串长度
#include <stdio.h>
int Strlen(const char* str)
{
//定义变量计数
int count = 0;
//利⽤指针遍历字符串
while (*str)
{
//计数加⼀
count++;
//字符串指针后移,遍历下⼀个字符
str++;
}
return count;
}
int main()
{
char arr[31] = { 0 };
//输⼊字符串
scanf("%[^\n]s", arr);
//获取字符串⻓度
int len = Strlen(arr);
printf("%d\n", len);
return 0;
}
解析:其中^表示"非",[^\n]
表示读入换行字符就结束读入。这个是scanf的正则用法,我们都知道scanf不能接收空格符,一接受到空格就结束读入,所以不能像gets()等函数一样接受一行字符串,但是使用%[^\n]
就可以读取一行,直到碰到’\n’才结束读入。
本题是对strlen的模拟实现,strlen本质就是指针读取,读取字符串首元素地址,没遇到\0时,地址加一,计数加一。遇到\0停止,并返回数量大小。
法二:利用递归求
#include <stdio.h>
//使⽤递归的⽅式解决
int Strlen(const char* str)
{
//递归结束条件
if (*str == '\0')
return 0;
else
//返回1加后⾯⼦字符串的和
return 1 + Strlen(str + 1);
}
int main()
{
char arr[31] = { 0 };
//输⼊字符串
scanf("%[^\n]s", arr);
//获取字符串⻓度
int len = Strlen(arr);
printf("%d\n", len);
return 0;
}
第十六题
输入⼀个字符串,写⼀个函数将⼀个字符串的内容逆序过来
#include <stdio.h>
#include <string.h>
void reverse(char* 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[31] = { 0 };
//输⼊字符串
scanf("%[^\n]s", arr);
//对字符串进⾏逆序
reverse(arr);
printf("%s\n", arr);
return 0;
}
解析:利用指针进行交换从而实现倒序
第十七题
输入⼀个整数m,求这个整数m的每⼀位之和,并打印
#include <stdio.h>
int digit_sum(int m)
{
//定义变量记录每⼀位的和
int s = 0;
//当前数还不为0,获取其个位数加到s中
while (m)
{
s += m % 10;
//删除其个位数
m /= 10;
}
//返回每⼀位的和
return s;
}
int main()
{
int m = 0;
//输⼊整数
scanf("%d", &m);
//获取每⼀位的和
int ret = digit_sum(m);
printf("%d\n", ret);
return 0;
}
解析: m%10 :取m的最后一位
m/10 :去除m的最后一位
第十八题
输入⼀个字符串,判断这个字符串是否是回文字符串
#include <stdio.h>
#include <string.h>
int is_palindrome_string(char arr[])
{
//获取字符串⻓度
int n = strlen(arr);
//定义两个指针分别指向字符串的⾸位和末位
char* left = arr;
char* right = arr + n - 1;
//当两个指针相对位置未发⽣变化时对其进⾏判断
while (left < right)
{
//两字符不相等,返回0,表⽰字符串不是回⽂串
if (*left != *right)
return 0;
//两指针相向移动⼀位
left++;
right--;
}
//返回1,表⽰字符串是回⽂串
return 1;
}
int main()
{
char arr[31];
//输⼊
scanf("%[^\n]s", arr);
//判断字符串是否为回⽂串
int ret = is_palindrome_string(arr);
if (ret == 1)
printf("Yes\n");
else
printf("No\n");
return 0;
}
第十九题
写⼀个函数my_strcpy,实现拷贝字符串的功能
#include <stdio.h>
void my_strcpy(char* dest, const char* src)
{
// 拷⻉源字符串的内容到⽬标字符串中
while (*src != '\0')
{
*dest = *src;
dest++;
src++;
}
// 拷⻉源字符串的结束标志
*dest = '\0';
}
int main()
{
char arr1[] = "hello bite";
char arr2[20] = { 0 };
//将字符串arr1中的内容拷⻉到字符串arr2中
my_strcpy(arr2, arr1);
//打印字符串arr2
printf("%s\n", arr2);
return 0;
}
第二十题
输入两个升序排列的序列,将两个序列合并为一个有序序列并输出。输入包含三行,第一行包含两个正整数n, m,用空格分隔。n表示第二行第⼀个升序序列中数字的个 数,m表示第三行第二个升序序列中数字的个数。其中 1<=n<=30, 1<=m<=30第二行包含 n 个整数,用空格分隔。第三行包含 m 个整数,用空格分隔。输出为一行,输出长度为n+m的升序序列,即长度为n的升序序列和长度为m的升序序列中的元素重 新进行升序序列排列合并。
#include <stdio.h>
int main() {
int n = 0;
int m = 0;
//输⼊//可以使用变长数组
scanf("%d %d", &n, &m);
int arr1[30] = {0};
int arr2[30] = {0};
int arr3[60] = {0};
//输⼊arr1
int i = 0;
for (i = 0; i < n; i++)
{
scanf("%d", &arr1[i]);
}
//输⼊arr2
for (i = 0; i < m; i++)
{
scanf("%d", &arr2[i]);
}
//合并arr1和arr2到arr3数组中
i = 0; //i遍历arr1
int j = 0;//j遍历arr2
int k = 0;//k遍历arr3
//合并两个数组的元素⾄arr3数组中
while (i < n && j < m)
{
//⽐较两个指针指向的元素⼤⼩
//arr1数组当前元素较⼩,将此值放⼊arr3数组并令i和k后移
if (arr1[i] < arr2[j])
{
arr3[k] = arr1[i];
i++;
k++;
}
//arr2数组当前元素较⼩,将此值放⼊arr3数组并令j和k后移
else
{
arr3[k] = arr2[j];
j++;
k++;
}
}
//若指针i越界,则arr1数组所有元素已经放⼊arr3数组
//arr2数组中有剩余元素,将它们依次放⼊arr3数组
if (i == n)
{
for (; j < m; j++)
{
arr3[k] = arr2[j];
k++;
}
}
//若指针j越界,则arr2数组所有元素已经放⼊arr3数组
//arr1数组中有剩余元素,将它们依次放⼊arr3数组
if (j == m)
{
for (; i < n; i++)
{
arr3[k] = arr1[i];
k++;
}
}
//打印
for (k = 0; k < m + n; k++)
{
printf("%d ", arr3[k]);
}
return 0;
}
解析:
第二十一题
实现一个函数,可以左旋字符串中的k个字符。
例如:
ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB
方法一
void leftRound(char * src, int time)
{
int i, j, tmp;
int len = strlen(src);
time %= len; //长度为5的情况下,旋转6、11、16...次相当于1次,7、12、17...次相当于2次,以此类推。
for (i = 0; i < time; i++) //执行k次的单次平移
{
tmp = src[0];
for(j = 0; j < len - 1; j++) //单次平移
{
src[j] = src[j + 1];
}
src[j] = tmp;
}
}
方法二
void leftRound(char * src, int time)
{
int len = strlen(src);
int pos = time % len; //断开位置的下标
char tmp[256] = { 0 }; //更准确的话可以选择malloc len + 1个字节的空间来做这个tmp
strcpy(tmp, src + pos); //先将后面的全部拷过来
strncat(tmp, src, pos); //然后将前面几个接上
strcpy(src, tmp); //最后拷回去
}
方法三
void reverse_part(char *str, int start, int end) //将字符串从start到end这一段逆序
{
int i, j;
char tmp;
for (i = start, j = end; i < j; i++, j--)
{
tmp = str[i];
str[i] = str[j];
str[j] = tmp;
}
}
void leftRound(char * src, int time)
{
int len = strlen(src);
int pos = time % len;
reverse_part(src, 0, pos - 1); //逆序前段
reverse_part(src, pos, len - 1); //逆序后段
reverse_part(src, 0, len - 1); //整体逆序
}
解析:还有一种方法使用双向链表,会在下一篇博客讲解。