一,return语句
1,return后边可以是一个数值,也可以是一个表达式,如果是表达式则先执行表达式,再返回表达式的结果,如:
int add(int a, int b)
{
return a + b;
}
case 1:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("ret = %d\n", ret);
break;
2,return后边也可以什么都没有,直接写return;这种写法适合函数返回类型是void的情况
3,return返回的值和函数返回类型不一致,系统将会自动将返回的值隐式转换为函数的返回类型
4,return语句执行中,函数就彻底返回,后边代码就不再执行
5,如果函数中存在if等分支的语句,则要保证每种情况下都有return返回,否则会出现编译错误
如:
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == ' ')
{
return;
}
else if (GetMineCount(mine,x,y)!= 0)
{
show[x][y] = GetMineCount(mine, x, y)+'0';
return;
}
二,assert宏的使用
在C语言中,`assert`是一个宏,用于在程序运行期间进行断言检查。它的基本语法如下:
#include <assert.h>
assert(expression);
在这里,`expression`是你想要检查的表达式。如果这个表达式的结果是`false`(也就是0),那么`assert`就会触发一个错误,并向标准错误输出(`stderr`)打印一条错误信息,然后调用`abort()`函数来终止程序的运行。
例如,假设我们有一个函数,该函数接受一个整数作为参数,并假设这个整数大于0。我们可以使用`assert`来确保这个条件得到满足:
#include <stdio.h>
#include <assert.h>
void do_something(int value) {
assert(value > 0); // 断言:value大于0
// ... 其他代码 ...
}
int main() {
do_something(2); // 这个调用将成功
do_something(0); // 这个调用将导致断言错误
return 0;
}
在这个例子中,如果`do_something`函数的参数是0,那么`assert`就会触发一个错误,程序将停止运行,并显示一条错误消息。
需要注意的是,`assert`主要用于调试目的。在程序发布时,为了提高性能,常常会通过定义`NDEBUG`宏来禁用`assert`。因此,依赖`assert`的代码在发布版本中可能不会工作。
让我们来举个栗子:
assert(P! = NULL);
上面代码在程序运行到这一行语句时,验证变量p是否等于NULL。如果确实不等于NULL,程序继续运行,否则将会终止运行,并且给出报错信息提示
使用assert()的好处:
①它不仅自动标识文件和出问题的行号,还有一种无需更改代码就能开启或关闭assert()的机制。
②如果已经确认程序没有问题,就不需要再做断言就在#include<assert.h>语句的前面定义一个宏NDEBUG。
#define NDEBUG
#include<assert.h>
然后,重新编译程序,编译器就会禁用文件中所有的assert()语句。如果程序又出现问题,可以移除这条#define NDEBUG指令(或者把它注释掉),再次编译,这样就重新启用了assert()语句
assert()的缺点是,因为引入了额外的检查,增加了程序的运行时间
一般我们可以再Debug中使用,再Release版本中选择禁用assert就行,再VS这样的集成开发环境中,再Release版本中,直接就是优化掉了。这样在debug版本写有利于程序员排查问题
三,字符串旋转结果
写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。
例如:给定s1 =AABCD和s2 = BCDAA,返回1
给定s1=abcd和s2=ACBD,返回0.
AABCD左旋一个字符得到ABCDA
AABCD左旋两个字符得到BCDAA
AABCD右旋一个字符得到DAABC
此题有三个解法:
解法一:
#define DEBUG
#include<stdio.h>
#include<string.h>
#include<assert.h>
void leftRound(char* str, int k)
{
int len = strlen(str);
int time = k % len;
for (int i = 0; i < time; i++)
{
char tmp = str[0];
int j = 0;
for (; j < len - 1; j++)
{
str[j] = str[j + 1];
}
str[j] = tmp;
}
}
int main()
{
char str[20] = "abcdef";
leftRound(str, 5);
printf("%s\n", str);
return 0;
}
解法二:
//第二种解法
void leftRound(char* str, int k)
{
int len = strlen(str);
int time = k % len;//2
char tmp[256] = { 0 };
strcpy(tmp, str + time);//tmp==cd
strncat(tmp, str, time);//cdab tmp存储的就是cdab
strcpy(str, tmp);//将tmp里面的所有内容都拷贝到str中
}
int main()
{
char str[20] = "abcdef";
leftRound(str, 7);
printf("%s\n", str);
//strcpy();字符串拷贝
//strncpy();字符串拼接->n个
/*char str1[10] = {0};
char* str2 = "abcd";
strcpy(str1, str2);//意思是把str2拷贝到str1中
printf("%s\n", str1);//abcd
printf("%s\n", str2);//abcd
char str3[100] = "abc";
char* str4 = "hello";
strcat(str3, str4);//把str4里面的所有内容拷贝到str3当中
strncat(str3, str4, 2);//拼接部分,拼接str4中的前面两个字母
printf("%s", str3);*/
return 0;
}
解法三:
void Reverse(char* str, int start, int end)
{
int left = start;
int right = end;
while (left < right)
{
char tmp = str[left];
str[left] = str[right];
str[right] = tmp;
left++;
right--;
}
}
void leftRound(char* str, int k)
{
int len = strlen(str);
int time = k % len;//2
Reverse(str, 0, time - 1);
Reverse(str, time, len - 1);
Reverse(str, 0, len - 1);
}
int main()
{
char str[20] = "abcdef";
leftRound(str, 7);
printf("%s\n", str);
}
四,调整数组使奇数全部都位于偶数前面。
思路:
1. 给定两个下标left和right,left放在数组的起始位置,right放在数组中最后一个元素的位置
2. 循环进行一下操作
a. 如果left和right表示的区间[left, right]有效,进行b,否则结束循环
b. left从前往后找,找到一个偶数后停止
c. right从后往前找,找到一个奇数后停止
d. 如果left和right都找到了对应的数据,则交换,继续a,
void funcction(int arr[], int len)
{
int left = 0;
int right = len - 1;
int tmp = 0;
while (left < right)
{
// 从前往后,找到一个偶数,找到后停止
while ((left < right) && arr[left] % 2 == 1)
{
left++;
}
// 从后往前找,找一个奇数,找到后停止
while ((left < right) && (arr[right] % 2 == 0))
{
right--;
}
// 如果偶数和奇数都找到,交换这两个数据的位置
// 然后继续找,直到两个指针相遇
if (left < right)
{
tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int len = sizeof(arr) / sizeof(arr[0]);
funcction(arr, len);
for (int i = 0; i < len; i++)
{
printf("%d ", arr[i]);
//printf("%d ", *(arr+i));
}
}
五,使用指针打印数组内容
#include <stdio.h>
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
//在这里完成代码
// 分析:因为数组中存储的元素类型是int类型的,因此只要给一个int的指针,依次取索引数组中的每个元素即可
int* p = arr; // 数组名代表数组首元素的地址
for(int i = 0; i < sizeof(arr)/sizeof(arr[0]); ++i)
{
printf("%d ", *p); // *p: 取到p所指向位置的元素
++p; // 获取p的下一个位置
}
return 0;
}
六,实现一个对整形数组的冒泡排序
/*
思路:
遍历数组,对数组中相邻的两个元素进行比较,如果需要升序,前一个数据大于后一个数据时,交换两个位置上的数据,直到所有的数据比较完,此时,最大的数据已经放在数组的末尾。
除最大数据已经排好序外,其余数据还是无需,对剩余数据采用与上述类似的方式进行处理即可
*/
void BubbleSort(int array[], int size)
{
// 外层循环控制冒泡排序的趟数
// size-1表示:最后一趟区间中只剩余1个元素,该趟冒泡可以省略
for(int i = 0; i < size-1; ++i)
{
// 具体冒泡的方式:用相邻的两个元素进行比较,前一个大于后一个元素时,交换着两个数据,依次直到数组的末尾
for(int j = 1; j < size-i; ++j)
{
if(array[j-1] > array[j])
{
int temp = array[j-1];
array[j-1] = array[j];
array[j] = temp;
}
}
}
}
int main()
{
int arr[10] = { 1,3,5,7,9,2,4,6,8,10 };
int size = sizeof(arr) / sizeof(arr[0]);
BubbleSort(arr, size);
for (int i = 0; i < size; i++)
{
printf("%d ", *(arr + i));
}
}
//优化:如果某次冒泡结束后,序列已经有序了,后面剩余元素的冒泡可以省略
void BubbleSort(int array[], int size)
{
// 外层循环控制冒泡排序的趟数
// size-1表示:最后一趟区间中只剩余1个元素,该趟冒泡可以省略
for (int i = 0; i < size - 1; ++i)
{
int isChange = 0;
// 具体冒泡的方式:用相邻的两个元素进行比较,前一个大于后一个元素时,交换着两个数据,依次直到数组的末尾
for (int j = 1; j < size - i; ++j)
{
if (array[j - 1] > array[j])
{
int temp = array[j - 1];
array[j - 1] = array[j];
array[j] = temp;
isChange = 1; // 如果本次冒泡进行数据交换了,说明本次还是无序的,就将isChange设置为1
}
}
// 如果本次冒泡中,元素没有交换,则本次开始冒泡时,数据已经有序了,后面的冒泡可以不用进行了
if (!isChange)
return;
}
}int main()
{
int array[10] = { 1,3,5,7,9,2,4,6,8,10 };
int size = sizeof(array) / sizeof(array[0]);
BubbleSort(array, size);
for (int i = 0; i < size; i++)
{
printf("%d ", *(array + i));
}
}
七,杨氏矩阵
要求:
有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在。
时间复杂度小于O(N);
思路:
我们仔细分析,不难发现,
对于杨氏矩阵老说,右上角和左下角的元素是有特点的。
右上角的元素是一行中最大的,一列中最小的。左下角的元素是一行中最小的,是一列中最大的。
所以我们可以从右上角或者左下角开始查找。
比如:从右上角开始查找的时候,右上角的元素比我们要查找元素小,我们就可以去掉右上角元素所在的这一行
右上角的元素比我们要查找的元素大,我们就可以去掉右上角元素所在的这一列。
然后依然找右上角的元素继续和要查找的元素与比较。这样每一次比较去掉一行或者去掉一列。
这个查找效率是高于遍历数组元素的
,所以时间复杂度是小于O(N),也满足题目要求。
int findnum(int a[][3], int x, int y, int f) //第一个参数的类型需要调整
{
int i = 0, j = y - 1; //从右上角开始遍历
while (j >= 0 && i < x)
{
if (a[i][j] < f) //比我大就向下
{
i++;
}
else if (a[i][j] > f) //比我小就向左
{
j--;
}
else
{
return 1;
}
}
return 0;
}
int main()
{
int a[][3] = { {1, 3, 5},
{3, 5, 7},
{5, 7, 9} }; //一个示例
if (findnum(a, 3, 3, 2))
{
printf("It has been found!\n");
}
else
{
printf("It hasn't been found!\n");
}
return 0;
}
八,猜凶手
内容:日本某地发生了一件谋杀案,警察通过排查确定杀人凶手必为4个嫌疑犯的一个。
以下为4个嫌疑犯的供词:
A说:不是我。
B说:是C。
C说:是D。
D说:C在胡说
已知3个人说了真话,1个人说的是假话。
现在请根据这些信息,写一个程序来确定到底谁是凶手。
//这个题就是按照正常方式,假设凶手是a,b,c,d其中的一个,看是不是满足题目条件,如果满足就找出了凶手。
#include<stdio.h>
int main()
{
int killer = 0;
//分别假设凶手是a,b,c,d,看谁是凶手时满足3个人说了真话,一个人说了假话
for (killer = 'a'; killer <= 'd'; killer++)
{
if ((killer != 'a') + (killer == 'c') + (killer == 'd') + (killer != 'd') == 3)
printf("凶手是:%c", killer);
}
return 0;
}
九,杨辉三角
由于此题要打印整个杨辉三角的数据而非取出某一项,所以不可避免的一定是要填出每一项,没有偷懒的余地,那就老老实实的根据规律填空即可。按照题设的场景,能发现数字规律为:d[i][j] = d[i - 1][j] + d[i - 1][j - 1]。所以我们只要按照这个方法填表即可。
void yangHuiTriangle(int n)
{
int data[30][30] = { 1 }; //第一行直接填好,播下种子
int i, j;
for (i = 1; i < n; i++) //从第二行开始填
{
data[i][0] = 1; //每行的第一列都没有区别,直接给1,保证不会越界。
for (j = 1; j <= i; j++) //从第二列开始填
{
data[i][j] = data[i - 1][j] + data[i - 1][j - 1]; //递推方程
}
}
for (i = 0; i < n; i++) //填完打印
{
for (j = 0; j <= i; j++)
{
printf("%d ", data[i][j]);
}
putchar('\n');
}
}
改进:
由于我在填第n行的杨辉三角时,只跟第n-1行的杨辉三角产生联系,不会跟之前的有联系,所以没必要保存每一行的杨辉三角,填一行打一行就行了,这样能让空间复杂度从n^2降低到n。但是在填数据的时候不能对之前的数据覆盖,所以需要从后向前填。而填杨辉三角顺序对结果是没有影响的,所以可以实现。
void yangHuiTriangle(int n)
{
int data[30] = { 1 };
int i, j;
printf("1\n"); //第一行就直接打印了
for (i = 1; i < n; i++) //从第二行开始
{
for (j = i; j > 0; j--) //从后向前填,避免上一行的数据在使用前就被覆盖
{
data[j] += data[j - 1]; //公式同上,由于变成了一维,公式也变简单了。
}
for (j = 0; j <= i; j++) //这一行填完就直接打印了。
{
printf("%d ", data[j]);
}
putchar('\n');
}
}
※这种方法虽然降低了空间复杂度,但只能保存最后一行的数据,不利于反复查询,两个填法各有各的适用场景。就本题而言,改进后的胜出。