阶段习题详解
字符串逆序
写一个函数,可以逆序一个字符串的内容。
//逆序函数
#include <string.h>
#include <assert.h>
void reverse(char *str) {
/*通过做指针和右指针互换。*/
assert(str);
int len = strlen(str);
char *left = str; //左指针是传进来的首元素的地址
char *right = str + len - 1;//右指针是左指针地址加上字符串长度,然后减去1。
while (left < right) {
// 两值交换
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
int main() {
char arr[200] = {0};
scanf("%s", arr);
//逆序函数
reverse(arr);
printf("%s", arr);
}
计算求和
求sum = a +aa+aaa+…的前n项的和,其中的a只是一个数字。
int main() {
int a = 0;
int n = 0;//前n项的n
scanf("%d%d", &a, &n);
int sum = 0;//求和
int i = 0;//计数
int res = 0;//计算出一个数。
for (i = 0; i < n; ++i) {
res = res * 10 + a;
sum += res;
}
printf("%d\n", sum);
return 0;
}
打印水仙花数
求出0~100000之间的所有水仙花数并输出。
“水仙花数”是指一个n位数,其各位数字的n次方之和恰好等于该数本身,如153 = 13+53+33,则153是一个水仙花数。
int main(){
//从0开始到100000进行遍历
for (int i = 0; i < 100000; i++) {
//判断是不是水仙花数
//1.统计位数。n位数。
int n = 1;
int tmp = i;
while (tmp/=10){
n++;
}
//2. 计算i的每一位的n次方之和sum
tmp = i;
int sum = 0;
while (tmp){
sum+=pow(tmp%10,n);
tmp/=10;
}
//3.比较i和sum
if(i == sum){
printf("%d ",i);
}
}
return 0;
}
打印菱形
用c语言在屏幕上打印以下的图案。
int main(){
int line = 7;
//打印上半部分
for (int i = 0; i < line; ++i) {
//空格部分
for (int j = 0; j < line-1-i; ++j) {
printf(" ");
}
//*部分
for (int j = 0; j < 2 * i + 1; ++j) {
printf("*");
}
//换行
printf("\n");
}
//下半部分
for (int i = 0; i < line - 1; ++i) {
//空格
for (int j = 0; j < i+1; ++j) {
printf(" ");
}
//"*"
for (int j = 0; j < 2*(line-i-1)-1; ++j) {
printf("*");
}
printf("\n");
}
return 0;
}
解析:这个题中的每次for循环的起始值为1或者0的时候,后面的条件判断也会有很大的变化。
喝汽水
喝汽水,1瓶汽水1元,2个空瓶可以换一瓶汽水,给20元,可以得多少汽水。
int main() {
int num = 20;//可以买(换)到汽水数
// scanf("%d",&num);//可以改为任意数,放开之前将num的初始值改为0。
int sum = 0;//计算总数
int bottle = 0;//空瓶数
sum = num;//总数累计开始
bottle = num;//喝完后当前的空瓶子
while (bottle >= 2) {
num = bottle / 2;//由空瓶换的汽水数
//更新空瓶数
if (bottle % 2 == 1) {
bottle = 1;
} else {
bottle = 0;
}
//更新总数
sum += num;
// 喝完已有的更新空瓶数
bottle += num;
//更新现有可乐数
num = 0;
}
printf("%d", sum);
return 0;
}
解析:将当前有的可乐数,当前空瓶数,已经有过的可乐数按照实际情况走一遍流程然后思考终止条件。
调整奇偶数顺序
调整数组使奇数全部位于偶数的前面。
输入一个整形数组,实现一个函数,
来调整该数组中数字的顺序使得数组中所有的技术位于数组的前半部分,所有的偶数位于数组的后半部分。
void move(int *arr, int sz) {
int *left = arr;
int *right = arr + sz - 1;//最右边的元素
while (1) {
//左边找到偶数停止。
while (*left % 2 == 1) {
left++;
}
//右边找到奇数停止。
while (*right % 2 == 0) {
right--;
}
// 如果找到的不是一个位置上的值交换两个值。
//这里的条件不能用不等于,在计算最后两个交换之后,会陷入死循环。
if (left < right) {
int tmp = *right;
*right = *left;
*left = tmp;
} else break;
}
}
void print(int *arr, int size) {
for (int i = 0; i < size; ++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;
}
解析:整体的思路就是从左边开始找奇数从右端开始找偶数,找到就交换。这样就可以将偶数放在后面,奇数放在前面了
杨辉三角
在屏幕上打印杨辉三角
int main() {
//初始化
int arr[10][10] = {0};
//杨辉三角形的建立
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
//将第一列赋值为1
if (j == 0) {
arr[i][j] = 1;
}
//将斜线赋值为1
if (i == j) {
arr[i][j] = 1;
}
//中间赋值
if ((i > 0) && (j > 0)) {
arr[i][j] = arr[i - 1][j - 1] + arr[i - 1][j];
}
}
}
//打印杨辉三角
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10-1-i; ++j) {
printf(" ");
}
for (int j = 0; j < 10; ++j) {
(j > i) ? printf(" ") : printf("%3d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
解析:第一步,想到应该使用什么来存放数据(数组),左边的空白就是最后打印的时候输出相应大小的空格长度即可。找出数之间的规律,先初始化,无法计算的数据,然后计算可以计算的数据并填入数组。最后进行打印即可。
猜凶手
日本某地发生异常谋杀案,警察通过排查确定杀人凶手四个嫌疑犯的一个。已知三人说了真话,1人说了假话。
以下是供词:A说:不是我。B说:是C。C说:是D。D说:C在胡说。
现在根据这些信息,写一个程序来确定谁是凶手。
int main(){
int killer = 0;
for(killer = 'a';killer<='d';killer++){
if((killer=='a')+(killer=='c')+(killer=='d')+(killer!='d')==3){
printf("killer is %c",killer);
}
}
return 0;
}
解析:将4个人说的话转化为逻辑判断语句。假设凶手是谁,然后判断此时四条语句的正确数。如果是3,那就说明我们要的答案是当前的条件上表示的谁是凶手。
猜名次
5位运动员参加了10米跳水比赛,有人让他们预测比赛结果:A选手说:B第二,我第三。B选手说:我第二,E第四。C选手说:我第一,D第二。D选手说:C最后,我第三。E选手说:我第四,A第一。比赛结束后每位选手都说对了一半,请编程确定比赛的名次。
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) {
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) &&
(a * b * c * d * e == 120))
printf("a=%d,b=%d,c=%d,d=%d,e=%d", a, b, c, d, e);
}
}
}
}
}
return 0;
}
解析:这个思路就相当于枚举,将所有的情况列举出来,筛选出满足条件的。
字符串左旋
实现一个函数,可以左旋转k个字符。
例如:
ABCD左旋转一个字符,BCDA
ABCD左旋转两个字符,CDAB
#include <string.h>
void left_move(char *arr,int k){
int len = strlen(arr);
//左旋k次
for (int i = 0; i < k; ++i) {
//左旋操作
//1.将第一个元素取出放入临时变量
char tmp = *arr;
//2.将剩余的变量进行向左移动一个单位。
for (int j = 0; j < len-1; ++j) {
*(arr+j) = *(arr+j+1);
}
//3.将第一个元素放在最后
*(arr+len-1) = tmp;
}
}
int main(){
char arr[] = "abcdef";
int k = 2;
left_move(arr,k);
printf("%s",arr);
return 0;
}
解析:这是暴力求解法。每一次向左移动一个字符。执行n次。
如果是右旋转,我们可以先将最右边的值放在临时变量中然后向右移动。
#include <string.h>
//逆序字符串的函数
void reverse(char* start, char *end) {
while (start<end){
char tmp = *start;
*start = *end;
*end = tmp;
start++;
end--;
}
}
void left_move(char *arr, int k) {
int len = strlen(arr);
k = k % len;
//1.逆序左边
reverse(arr, arr + k - 1);
//2.逆序右边
reverse(arr + k, arr + len - 1);
//3.逆序整体
reverse(arr, arr + len - 1);
}
解析:将字符串进行三次翻转。因为旋转后的字符串在小范围内都是存在一定顺序的,然后,对于一段字符而言,经过两次就可以恢复顺序,所以有了总共翻转三次的思想。
字符串旋转结果
写一个函数,判断一个字符串中是否为另一个字符串旋转后的字符串。
例如:给定s1=AABCD和s2=BCDAA,返回1
给定s1 = abcd 和 s2 = acbd,返回0。
#include <string.h>
//逆序字符串的函数
void reverse(char *start, char *end) {
while (start < end) {
char tmp = *start;
*start = *end;
*end = tmp;
start++;
end--;
}
}
void left_move(char *arr, int k) {
int len = strlen(arr);
k = k % len;
//1.逆序左边
reverse(arr, arr + k - 1);
//2.逆序右边
reverse(arr + k, arr + len - 1);
//3.逆序整体
reverse(arr, arr + len - 1);
}
int is_move(char *s1, const char *s2) {
int len = strlen(s1);
for (int i = 0; i < len - 1; ++i) {
left_move(s1, 1);
int res = strcmp(s1, s2);
if (res == 1) {
return 1;
}
}
return 0;
}
int main() {
char arr1[] = "abcdef";
char arr2[] = "cdefab";
int res = is_move(arr1, arr2);
if (res == 1) {
printf("%s是%s旋转后的字符串。", arr2, arr1);
} else printf("%s不是%s旋转后的字符串。", arr2, arr1);
}
解析:这题的主要功能是实现is_move函数,剩下的功能都和上一题字符串左旋相同。这个解法也是相当于将所有的旋转后的情况拿出来和给定字符串进行对比。其实还有其他的方法。
int is_move(char *s1, char *s2) {
// 直接解决掉长度不符合的情况
int len1 = strlen(s1);
int len2 = strlen(s2);
if(len1!=len2) return 0;
//1.在str1字符串中追加一个str1字符串
strncat(s1,s1, len1);//字符产拼接函数,不能使用strcat
// 2.判断str2指向的字符串是否是str1指向的子串
// strstr是找子串的
char * res = strstr(s1,s2);
if(res == NULL)return 0;
else return 1;
}
int main() {
char arr1[30] = "abcdef";
char arr2[] = "cdefacb";
int res = is_move(arr1, arr2);
if (res == 1) {
printf("%s是%s旋转后的字符串。", arr2, arr1);
} else printf("%s不是%s旋转后的字符串。", arr2, arr1);
}
解析:在原字符串的最后追加一个自己。然后,在新生成的字符串中寻找另一个字符串是否为新生成的子串。
杨氏矩阵
有一个数字数组,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在。
要求:时间复杂度是小于O(n)。
提示:一行中的最大值,或者一列中的最大值可以直接排除一行或者一列。当然最小值也是可以的。那我我们为了一下子可以排除掉一行我们可以取左下角或者右上角,因为这两个位置,同时具有一个最大值和一个最小值的特性。
int FindNum(int arr[4][4], int k, int row, int col) {
//首先定位到的是右上角的元素。
int x = 0;
int y = col - 1;
while ((x >= 0 && x < row) && (y >= 0 && y < col)) {
//相等的情况->找到了
if (arr[x][y] == k) return 1;
//大于的情况
else if (arr[x][y] > k) y--;
//小于的情况
else if (arr[x][y] < k) x++;
}
// 没找到
return 0;
}
//杨氏矩阵
int main() {
int arr[4][4] = {{1, 2, 3, 4},
{4, 5, 6, 7},
{7, 8, 9, 10},
{8, 9, 12, 13}};
int k = 8;//我们要找的值
int row = sizeof(arr) / sizeof(arr[0]);//获取行数
int col = sizeof(arr[0]) / sizeof(arr[0][0]);//获取列数
int res = FindNum(arr, k, row, col);
if (res == 1)printf("找到了\n");
else printf("没有找到\n");
return 0;
}
注:如果要在函数外部进行访问下标的时候,可以将x、y定义在函数之外,将其指针传进函数,然后在外部访问输出。(这就是基本功的练习了。)