mirror of
https://github.com/hairrrrr/C-CrashCourse.git
synced 2026-02-11 22:44:57 +08:00
4-22
This commit is contained in:
1
code/Exercises/CMOOC/readme.md
Normal file
1
code/Exercises/CMOOC/readme.md
Normal file
@@ -0,0 +1 @@
|
||||
新建文件夹
|
||||
1
code/Exercises/C_Crash_Couse/readme.md
Normal file
1
code/Exercises/C_Crash_Couse/readme.md
Normal file
@@ -0,0 +1 @@
|
||||
新建文件夹
|
||||
1
code/Exercises/advanced C/readme.md
Normal file
1
code/Exercises/advanced C/readme.md
Normal file
@@ -0,0 +1 @@
|
||||
新建文件夹
|
||||
29
code/Exercises/advanced C/test/01/01 打印杨辉三角/test1.c
Normal file
29
code/Exercises/advanced C/test/01/01 打印杨辉三角/test1.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include<stdio.h>
|
||||
|
||||
#define HEIGHT 10 //假设杨辉三角的高度是10
|
||||
|
||||
int main(void) {
|
||||
|
||||
int YHtrangle[HEIGHT][HEIGHT] = { 0 };
|
||||
int i, j;
|
||||
|
||||
//计算杨辉三角
|
||||
for (i = 0; i < HEIGHT; i++) {
|
||||
|
||||
YHtrangle[i][0] = 1;//每行第一个元素为 1
|
||||
YHtrangle[i][i] = 1;//每行最后一个元素为 1
|
||||
//需要计算的是从第三行开始,i == 2
|
||||
for (j = 1; j < i; j++)
|
||||
YHtrangle[i][j] = YHtrangle[i - 1][j - 1] + YHtrangle[i - 1][j];// 比如第三行的 2 是 第二行 1 + 1的和,这是杨辉三角的规律
|
||||
}
|
||||
|
||||
//输出杨辉三角
|
||||
for (i = 0; i < HEIGHT; i++) {
|
||||
for (j = 0; j <= i; j++) {
|
||||
printf("%d ", YHtrangle[i][j]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
37
code/Exercises/advanced C/test/01/01 打印杨辉三角/test2.c
Normal file
37
code/Exercises/advanced C/test/01/01 打印杨辉三角/test2.c
Normal file
@@ -0,0 +1,37 @@
|
||||
#include<stdio.h>
|
||||
|
||||
#define HEIGHT 10 //如果要修改杨辉三角的高度修改这个参数即可
|
||||
|
||||
#define ARRLEN HEIGHT + 1
|
||||
|
||||
void printYH(int arr[][ARRLEN]) {
|
||||
|
||||
int row, col;
|
||||
|
||||
//这个程序比较方便惯性思维理解,数组的首行首列不用(下标从 1 开始)
|
||||
for (row = 1; row <= HEIGHT; row++) {
|
||||
|
||||
for (col = 1; col <= row; col++) {
|
||||
|
||||
if (col == 1 || col == row)
|
||||
arr[row][col] = 1;
|
||||
else
|
||||
arr[row][col] = arr[row - 1][col - 1] + arr[row - 1][col];
|
||||
|
||||
//直接输出计算结果不需要再用另一个循环输出
|
||||
printf("%d ", arr[row][col]);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
//当然这个程序也有缺点,如果你在程序中使用这个数组,你需要时刻记得下标为 0 是不存任何有意义的数据的
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
||||
int YHtrangle[ARRLEN][ARRLEN] = { 0 };
|
||||
|
||||
printYH(YHtrangle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
12
code/Exercises/advanced C/test/01/01 打印杨辉三角/题目.md
Normal file
12
code/Exercises/advanced C/test/01/01 打印杨辉三角/题目.md
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
#### 1.打印杨辉三角
|
||||
|
||||
```c
|
||||
1
|
||||
1 1
|
||||
1 2 1
|
||||
1 3 3 1
|
||||
1 4 6 4 1
|
||||
...
|
||||
```
|
||||
|
||||
63
code/Exercises/advanced C/test/01/02 字符串旋转/test1.c
Normal file
63
code/Exercises/advanced C/test/01/02 字符串旋转/test1.c
Normal file
@@ -0,0 +1,63 @@
|
||||
#include<stdio.h>
|
||||
#include<string.h>
|
||||
#include<assert.h>
|
||||
|
||||
int strJudge(const char* str1, const char* str2) {
|
||||
|
||||
char* str2_start = (char*)str2;
|
||||
|
||||
//str1 和 str2 不能是空指针
|
||||
assert(str1 != NULL && str2 != NULL);
|
||||
|
||||
//如果字符串长度是 1 ,这个判断就没有意义了
|
||||
assert(strlen(str1) != 1);
|
||||
|
||||
//如果连个字符串长度都不一样,肯定不是
|
||||
if (!(strlen(str1) == strlen(str2)))
|
||||
return 0;
|
||||
|
||||
//在 str2 中寻找 str1 的第一个元素
|
||||
while (*str2) {
|
||||
if (*str2 == *str1) {
|
||||
++str1;// str1 中的第一个元素已经找到,在下一个循环寻找 str1 中的下一个
|
||||
break;
|
||||
}
|
||||
|
||||
++str2;
|
||||
}
|
||||
//没有找到,直接返回 0
|
||||
if (*str2 == '\0')
|
||||
return 0;
|
||||
|
||||
while (*str1) {
|
||||
|
||||
++str2;
|
||||
|
||||
if (*str2 == '\0')
|
||||
str2 = str2_start;
|
||||
|
||||
if (!(*str2 == *str1))
|
||||
return 0;
|
||||
|
||||
++str1;
|
||||
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
||||
char str1[100];
|
||||
char str2[100];
|
||||
|
||||
printf("Enter two strings: ");
|
||||
scanf("%s %s", str1, str2);
|
||||
|
||||
if (strJudge(str1, str2))
|
||||
printf("Yes\n");
|
||||
else
|
||||
printf("No\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
12
code/Exercises/advanced C/test/01/02 字符串旋转/题目.md
Normal file
12
code/Exercises/advanced C/test/01/02 字符串旋转/题目.md
Normal file
@@ -0,0 +1,12 @@
|
||||
#### 2. 字符串旋转
|
||||
|
||||
|
||||
|
||||
写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。
|
||||
|
||||
例如:给定s1 =AABCD和s2 = BCDAA,返回1
|
||||
给定s1=abcd和s2=ACBD,返回0.
|
||||
|
||||
AABCD左旋一个字符得到ABCDA
|
||||
AABCD左旋两个字符得到BCDAA
|
||||
AABCD右旋一个字符得到DAABC
|
||||
56
code/Exercises/advanced C/test/01/03 字符串左旋/test1.c
Normal file
56
code/Exercises/advanced C/test/01/03 字符串左旋/test1.c
Normal file
@@ -0,0 +1,56 @@
|
||||
//实现一个函数,可以左旋字符串中的k个字符。
|
||||
//
|
||||
//例如:
|
||||
//ABCD左旋一个字符得到BCDA
|
||||
//ABCD左旋两个字符得到CDAB
|
||||
|
||||
#include<stdio.h>
|
||||
#include<string.h>
|
||||
#include<assert.h>
|
||||
|
||||
char* strRightReverse(char* str, int num) {
|
||||
|
||||
char strAfterReverse[100] = {0};
|
||||
char* strReverse = str;
|
||||
char* strNotReverse = str;
|
||||
int i;
|
||||
int length = strlen(str);
|
||||
|
||||
if (num < 0 || num > strlen(str)) {
|
||||
printf("Illegal input!\n");
|
||||
return str;
|
||||
}
|
||||
|
||||
//我们先将不左旋的数放进 strReverse 数组中
|
||||
strNotReverse += num;
|
||||
|
||||
for (i = 0; i < length - num; i++)
|
||||
strAfterReverse[i] = *strNotReverse++;
|
||||
|
||||
//再将要左旋的数依次放入数组尾
|
||||
for (i = length - num; i < length; i++) {
|
||||
strAfterReverse[i] = *strReverse++;
|
||||
}
|
||||
strAfterReverse[length] = '\0';
|
||||
|
||||
strcpy(str, strAfterReverse);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
||||
char str[100];
|
||||
int num = 0;
|
||||
|
||||
printf("Enter a string: ");
|
||||
scanf("%s", str);
|
||||
printf("Enter a number: ");
|
||||
scanf("%d", &num);
|
||||
|
||||
strRightReverse(str, num);
|
||||
|
||||
printf("output: %s\n", str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
85
code/Exercises/advanced C/test/01/04 杨氏矩阵/test1.c
Normal file
85
code/Exercises/advanced C/test/01/04 杨氏矩阵/test1.c
Normal file
@@ -0,0 +1,85 @@
|
||||
//#### 4. 杨氏矩阵
|
||||
//
|
||||
//有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在。
|
||||
//
|
||||
//
|
||||
//
|
||||
//要求:时间复杂度小于O(N);
|
||||
//
|
||||
//*可以先不去管复杂度问题,这里给出一种方便理解的算法。[参考文章](https://blog.csdn.net/sgbfblog/article/details/7745450?depth_1-utm_source=distribute.pc_relevant_right.none-task&utm_source=distribute.pc_relevant_right.none-task)*
|
||||
|
||||
|
||||
#include<stdio.h>
|
||||
#include<assert.h>
|
||||
|
||||
#define ROW 3
|
||||
#define COL 3
|
||||
|
||||
void printArr(int arr[ROW][COL]);
|
||||
int searchYangMatri(int arr[ROW][COL], int row, int col, int target, int position[2]);
|
||||
|
||||
int main(void) {
|
||||
|
||||
int target;
|
||||
int position[2] = {-1, -1};//目标数在矩阵中的位置
|
||||
|
||||
//注意:杨氏矩阵并不是有序数列
|
||||
int arr[ROW][COL] = {
|
||||
{1, 3, 5},
|
||||
{2, 4, 6},
|
||||
{7, 8, 9},
|
||||
};
|
||||
|
||||
printArr(arr);
|
||||
|
||||
printf("Enter a target: ");
|
||||
scanf("%d", &target);
|
||||
|
||||
if (searchYangMatrix(arr, ROW, COL, target, position))
|
||||
printf("%d is found!The position in the matrix is as follow:(%d, %d)\n", target, position[0], position[1]);
|
||||
else
|
||||
printf("Not Found!\n");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void printArr(int arr[ROW][COL]) {
|
||||
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < ROW; i++) {
|
||||
for (j = 0; j < COL; j++)
|
||||
printf("%d ", arr[i][j]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int searchYangMatrix(int arr[ROW][COL], int row, int col, int target, int position[2]) {
|
||||
|
||||
assert(arr != NULL);
|
||||
|
||||
//从矩阵的右上角开始搜寻
|
||||
int i = 0;
|
||||
int j = col - 1;
|
||||
|
||||
while (i < ROW && j >= 0) {
|
||||
|
||||
//如果目标数比右上角的数大,则去掉这一行(因为右上角的数是这一行中最大的)
|
||||
if (target > arr[i][j])
|
||||
i++;
|
||||
//如果目标数比右上角的小,则去掉这一列(因为右上角的数是这一列中最小的)
|
||||
else if (target < arr[i][j])
|
||||
j--;
|
||||
else {
|
||||
position[0] = j;//如果将数组右下角的元素放在二位坐标系的原点上, x 和数组的 列是对应的:x = col(元素所在位置的 col)
|
||||
position[1] = row - 1 - i;// y 与 数组行的关系是 y = ROW - 1 - row(元素所在位置的 row)
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
52
code/Exercises/advanced C/test/01/05 qsort实现/test1.c
Normal file
52
code/Exercises/advanced C/test/01/05 qsort实现/test1.c
Normal file
@@ -0,0 +1,52 @@
|
||||
#include<stdio.h>
|
||||
|
||||
//定义一个 Cmp 类型的函数指针,函数指针返回值是 int,参数是(int, int)
|
||||
typedef int(*Cmp)(int, int);
|
||||
|
||||
void bubbleSort(int arr[], int size, Cmp cmp);
|
||||
int Asc(int x, int y);
|
||||
int Desc(int x, int y);
|
||||
|
||||
int main(void) {
|
||||
|
||||
int arr[] = { 9, 5, 2, 7 };
|
||||
int size = sizeof(arr) / sizeof(arr[0]);
|
||||
int i = 0;
|
||||
|
||||
bubbleSort(arr, size, Desc);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
printf("%d ", arr[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bubbleSort(int arr[], int size, Cmp cmp) {
|
||||
|
||||
int i, j;
|
||||
int tmp;
|
||||
|
||||
for (i = 0; i < size - 1; i++) {
|
||||
for (j = 0; j < size - 1 - i; j++) {
|
||||
//根据 cmp 函数的规则,如果返回 1,进行 if 语句中的位置互换
|
||||
if (cmp(arr[j], arr[j + 1]) == 1) {
|
||||
tmp = arr[j];
|
||||
arr[j] = arr[j + 1];
|
||||
arr[j + 1] = tmp;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//升序
|
||||
int Asc(int x, int y) {
|
||||
//如果第一个参数比第二个参数大返回 1
|
||||
return (x > y ? 1 : 0);
|
||||
}
|
||||
|
||||
//降序
|
||||
int Desc(int x, int y) {
|
||||
return (x < y ? 1 : 0);
|
||||
}
|
||||
37
code/Exercises/advanced C/test/01/06 猜凶手/test1.c
Normal file
37
code/Exercises/advanced C/test/01/06 猜凶手/test1.c
Normal file
@@ -0,0 +1,37 @@
|
||||
//#### 6. 猜凶手日本某地发生了一件谋杀案,警察通过排查确定杀人凶手必为4个嫌疑犯的一个。
|
||||
//
|
||||
//以下为4个嫌疑犯的供词:
|
||||
//
|
||||
//
|
||||
//
|
||||
//A说:不是我。
|
||||
//
|
||||
//B说:是C。
|
||||
//
|
||||
//C说:是D。
|
||||
//
|
||||
//D说:C在胡说
|
||||
//
|
||||
//已知3个人说了真话,1个人说的是假话。
|
||||
//
|
||||
//
|
||||
//
|
||||
//现在请根据这些信息,写一个程序来确定到底谁是凶手。
|
||||
|
||||
#include<stdio.h>
|
||||
|
||||
int main(void) {
|
||||
|
||||
int i, murder;
|
||||
|
||||
//这种类型的问题思路是穷举——即遍历所有可能
|
||||
|
||||
for (murder = 'A'; murder <= 'D'; murder++) {
|
||||
// 3 人说了真话, 1 人说了假话,说明这四个语句 3 真 1 假 ,相加为 3
|
||||
if (((murder != 'A') + (murder == 'C') + (murder == 'D') + (murder != 'D')) == 3 )
|
||||
|
||||
printf("murder is %c\n", murder);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
48
code/Exercises/advanced C/test/01/07 猜名次/test1.c
Normal file
48
code/Exercises/advanced C/test/01/07 猜名次/test1.c
Normal file
@@ -0,0 +1,48 @@
|
||||
//#### 7. 猜名次
|
||||
//
|
||||
//5位运动员参加了10米台跳水比赛,有人让他们预测比赛结果:
|
||||
//
|
||||
//A选手说:B第二,我第三;
|
||||
//
|
||||
//B选手说:我第二,E第四;
|
||||
//
|
||||
//C选手说:我第一,D第二;
|
||||
//
|
||||
//D选手说:C最后,我第三;
|
||||
//
|
||||
//E选手说:我第四,A第一;
|
||||
//
|
||||
//比赛结束后,每位选手都说对了一半,请编程确定比赛的名次。
|
||||
|
||||
#include<stdio.h>
|
||||
|
||||
int main(void) {
|
||||
|
||||
int a, b, c, d, e;
|
||||
|
||||
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) {
|
||||
|
||||
//名次不能重复
|
||||
if (a * b * c * d * e == 120)
|
||||
printf("A 的名次:%d\nB 的名次:%d\nC 的名次:%d\nD 的名次:%d\nE 的名次:%d\n", a, b, c, d, e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
77
code/Exercises/advanced C/test/02/01 实现atoi/MyAtoi.c
Normal file
77
code/Exercises/advanced C/test/02/01 实现atoi/MyAtoi.c
Normal file
@@ -0,0 +1,77 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
// atoi 实现
|
||||
// ElementType atoi( const char *str );
|
||||
//转译 str 所指的字节字符串中的整数值。
|
||||
|
||||
//舍弃任何空白符,直至找到首个非空白符,然后接收尽可能多的字符以组成合法的整数表示,并转换之为整数值。合法的整数值含下列部分:
|
||||
|
||||
//(可选) 正或负号
|
||||
//数位
|
||||
|
||||
#include<stdio.h>
|
||||
#include<stdlib.h>
|
||||
|
||||
#define MAXSIZE 100
|
||||
|
||||
typedef long long ElementType;
|
||||
|
||||
ElementType MyAtoi(const char* str);
|
||||
ElementType StrToNum(int numbers[], int size);
|
||||
|
||||
int main(void) {
|
||||
|
||||
char str[MAXSIZE];
|
||||
ElementType NumInStr;
|
||||
|
||||
scanf("%s", str);
|
||||
|
||||
NumInStr = MyAtoi(str);
|
||||
|
||||
printf("%lld\n", NumInStr);// 转换成大的类型需要改变 printf 的转换说明
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ElementType MyAtoi(const char* str) {
|
||||
|
||||
ElementType ret = 0;
|
||||
int isFirstNumber = 1;
|
||||
int flg = 1;
|
||||
int numbers[MAXSIZE];
|
||||
int size = 0;
|
||||
|
||||
while (*str != '\0') {
|
||||
|
||||
if (*str >= '0' && *str <= '9') {
|
||||
// 判断这个数是否为第一个出现的数字,如果是,我们要判断前一个字符是否是 - 号
|
||||
if (isFirstNumber) {
|
||||
if (*(str - 1) == '-')
|
||||
flg = -1;
|
||||
isFirstNumber = 0;
|
||||
}
|
||||
// 直接将字符转化为数字放入数组内,数组放置的是目标数字的每一位(不带权重)。
|
||||
numbers[size++] = *str - '0';// 数字对应的 ACSII 码值减去 ‘0’对应的 ASCII 值就是实际上数组表示的值
|
||||
}
|
||||
++str;
|
||||
}
|
||||
|
||||
ret = StrToNum(numbers, size);
|
||||
|
||||
return (flg * ret);
|
||||
}
|
||||
|
||||
ElementType StrToNum(int numbers[], int size) {
|
||||
|
||||
ElementType ret = 0;
|
||||
ElementType weight = 1;
|
||||
int i;
|
||||
|
||||
for (i = size - 1; i >= 0; i--) {
|
||||
ret += numbers[i] * weight;
|
||||
weight *= 10;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
82
code/Exercises/advanced C/test/02/01 实现atoi/MyAtoi2.c
Normal file
82
code/Exercises/advanced C/test/02/01 实现atoi/MyAtoi2.c
Normal file
@@ -0,0 +1,82 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
// 这是 atoi 实现的另一个版本
|
||||
// 上一个版本其实和库里面的 atoi 实现有的地方不是一致的
|
||||
// 比如库函数 atoi 处理字符串 "abc123", 返回的结果是 0 ,而上一个我实现的 MyAtoi 会返回 123
|
||||
// "+-123", 返回的结果是 0, 而MyAtoi 会返回 -123
|
||||
// 其实我觉得我都 atoi 函数更强大哈哈
|
||||
// 但是我们也应该规范一点,毕竟是库函数
|
||||
|
||||
#include<stdio.h>
|
||||
#include<stdlib.h>
|
||||
#include<ctype.h>
|
||||
|
||||
int MyAtoi_2(const char* str) {
|
||||
|
||||
int flg = 1;
|
||||
int ret = 0;
|
||||
|
||||
if (str == NULL || str == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 判断是否为空白字符(空格,换行,制表,翻页 ...)
|
||||
while (isspace(*str)) {
|
||||
str++;
|
||||
}
|
||||
|
||||
// 判断是否为正负号
|
||||
// 用库函数 atoi 试一下就知道 "+-123" 这种字符串是无法转成数字的,所以这是一种要处理的情况
|
||||
// 如果符号位的下一个不是数字,则返回 0
|
||||
if (*str == '+' || *str == '-' ) {
|
||||
|
||||
if (*(str + 1) == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!isdigit(*(str + 1))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*str == '-') {
|
||||
flg = -1;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
|
||||
// 处理数字字符
|
||||
while (*str != '\0') {
|
||||
if (isdigit(*str)) {
|
||||
|
||||
// 判断一下会不会超过 int 的范围
|
||||
if (((unsigned long)ret * 10 + (*str - '0')) > INT_MAX) {
|
||||
return INT_MAX;
|
||||
}
|
||||
ret = ret * 10 + (*str - '0');
|
||||
|
||||
// 在 MyAtoi 中,我选择用数组存储每一位,然后从后向前依次乘以每一位的权重
|
||||
// 但是这样直接加一个数之前先乘以 10 的做法更为简洁
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
|
||||
return ret * flg;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
||||
char str[100];
|
||||
int ret;
|
||||
|
||||
scanf("%s", str);
|
||||
|
||||
ret = MyAtoi_2(str);
|
||||
|
||||
printf("%d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
// strncat 实现
|
||||
|
||||
#include<stdio.h>
|
||||
#include<assert.h>
|
||||
|
||||
char* MyStrncat(char* dest, const char* src, int count);
|
||||
|
||||
int main(void) {
|
||||
|
||||
char dest[100];
|
||||
char src[50];
|
||||
int count = 0;
|
||||
|
||||
printf("Enter string dest: ");
|
||||
scanf("%s", dest);
|
||||
printf("Enter string src: ");
|
||||
scanf("%s", src);
|
||||
printf("Enter number of character(s) copy from src to dest: ");
|
||||
scanf("%d", &count);
|
||||
|
||||
MyStrncat(dest, src, count);
|
||||
|
||||
printf("%s\n", dest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 其实我觉得这取实现程序不如不写。这种写法你上网一查都是一大片一大片的,
|
||||
// 乍一看感觉是对的,但是自己想一想,这是很危险的写法。当让你也可以归结于
|
||||
// C语言本身的语法不好,但是其实是我们的水品还没到罢了,我们想不到更好的写法,
|
||||
// 我们根本没学精 C!但是这糟糕的函数是 C 的创始人写出来的啊!似乎可以为我们找一些接口。
|
||||
|
||||
// 现在我们来说一下这样的写法有什么漏洞,首先,我们看一看 cppreference 上给的定义:
|
||||
|
||||
//后附来自 src 所指向的字符数组的至多 count 个字符,到 dest 所指向的空终止字节字符串的末尾,
|
||||
//若找到空字符则停止。字符 src[0] 替换位于 dest 末尾的空终止符。
|
||||
//始终后附终止空字符到末尾(故函数可写入的最大字节数是 count + 1 )。
|
||||
|
||||
//若目标数组没有对于 dest 和 src 的首 count 个字符加上终止空字符的足够空间,
|
||||
//则行为未定义。若源与目标对象重叠,则行为未定义。若 dest 不是指向空终止字节字符串的指针,
|
||||
//或 src 不是指向字符数组的指针,则行为未定义。
|
||||
|
||||
// 现在我来说一下我认为这个程序可能存在的问题:
|
||||
// 1.dest 函数本身加上 count 个字符是否会造成数组越界?
|
||||
// 2.dest 函数有没有 '\0' 我没有检测。如果没有,则很有可能造成越界,并且将造成非法内存访问和改写。
|
||||
// 3.如果 src 没有 '\0' 而 count 又大于 src字符数组有意义内容的大小,或者 src字符数组本身的大小,
|
||||
// 这时也会造成非法内存访问。
|
||||
// 4.src 是不是指向字符数组的指针?
|
||||
|
||||
// 如果还有问题欢迎补充,如果你有更好的解法(C语言)欢迎告诉我,我会加上你的真知灼见!(私信我即可)
|
||||
|
||||
char* MyStrncat(char* dest, const char* src, int count) {
|
||||
|
||||
assert(dest && src);
|
||||
|
||||
char* NewStr = dest;
|
||||
|
||||
// 循环结束 NewStr 指向 dest 字符串 '\0' 所在位置
|
||||
while (*NewStr != '\0') {
|
||||
++NewStr;
|
||||
}
|
||||
while (count-- && (*NewStr++ = *src++));
|
||||
|
||||
*NewStr = '\0';
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
#include<stdio.h>
|
||||
#include<assert.h>
|
||||
|
||||
char* MyStrncpy(char* dest, char* src, int count);
|
||||
|
||||
int main(void) {
|
||||
|
||||
char dest[100];
|
||||
char src[50];
|
||||
int count = 0;
|
||||
|
||||
printf("Enter string dest: ");
|
||||
scanf("%s", dest);
|
||||
printf("Enter string src: ");
|
||||
scanf("%s", src);
|
||||
printf("Enter number of character(s) copy from src to dest: ");
|
||||
scanf("%d", &count);
|
||||
|
||||
MyStrncpy(dest, src, count);
|
||||
|
||||
printf("%s\n", dest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 这个函数同样是考虑不周全的:
|
||||
// 1. count 如果大于 字符数组dest 怎么办?
|
||||
// 2. count 大于 src 而 src 没有空字符怎么办?
|
||||
|
||||
char* MyStrncpy(char* dest,const char* src, int count) {
|
||||
|
||||
assert(dest && src);
|
||||
char* begin = dest;
|
||||
|
||||
while (count-- && (*begin++ = *src++));
|
||||
|
||||
// count 如果小于 src 的长度,那么得到的字符串是不含空字符的。
|
||||
// 加上 \0 是保险的。
|
||||
if (count + 1 == 0) {
|
||||
*begin = '\0';
|
||||
}
|
||||
else {
|
||||
while (count--) {
|
||||
*begin++ = *src++;
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
77
code/Exercises/advanced C/test/02/03 单身狗问题/SingleDog.c
Normal file
77
code/Exercises/advanced C/test/02/03 单身狗问题/SingleDog.c
Normal file
@@ -0,0 +1,77 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
// 找单身狗问题:
|
||||
// 1. 现有一个数组,只有一个元素只出现了一次,其他元素都出现了两次,请找出这个元素。
|
||||
// 2. 如果数组中有两个元素只出现了一次,其他都出现了两次,请找出这两个元素。
|
||||
|
||||
// 这两个问题都应用到了按位异或
|
||||
// 如果两个相同的数按位异或结果为零
|
||||
// 我们在前面的 两数平均值求法 中用异或来求出两数 二进制位 中 不同的位 表示的数的 和
|
||||
|
||||
|
||||
#include<stdio.h>
|
||||
|
||||
int FindOneSingleDog(int arr[], int size) {
|
||||
|
||||
int Dog = arr[0];
|
||||
|
||||
for (int i = 1; i < size; i++) {
|
||||
Dog = Dog ^ arr[i];
|
||||
}
|
||||
|
||||
return Dog;
|
||||
}
|
||||
|
||||
void FindTwoSingleDog(int arr[], int size, int* Dog1, int* Dog2) {
|
||||
|
||||
int rst = arr[0];
|
||||
int leftPos = 0;
|
||||
|
||||
for (int i = 1; i < size; i++) {
|
||||
rst = rst ^ arr[i];
|
||||
}
|
||||
//此时 rst 等价于 Dog1 ^ Dog2, 这个结果的比特位一定有一个是 1
|
||||
|
||||
// 找到第一个是 1 的比特位
|
||||
for (leftPos = 0; leftPos < 32; leftPos++) {
|
||||
if ((rst & (1 << leftPos)) == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 将数组元素分成两部分,一部分 leftPos 位为 1,另一部分不为 1
|
||||
// 那么 Dog1 和 Dog2 就在不同的组内了。再次按位异或,结果就是 Dog1 和 Dog2 的值
|
||||
|
||||
*Dog1 = 0;
|
||||
*Dog2 = 0;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
// leftPos 为 1 的组
|
||||
if ((arr[i] & (1 << leftPos)) == 1) {
|
||||
*Dog1 = *Dog1 ^ arr[i];
|
||||
}
|
||||
else {
|
||||
*Dog2 = *Dog2 ^ arr[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
||||
int arr1[] = { 1, 2, 3, 4, 1, 2, 3 };
|
||||
int size1 = sizeof(arr1) / sizeof(arr1[0]);
|
||||
int Dog;
|
||||
|
||||
int arr2[] = { 1, 1, 2, 2, 3, 4 };
|
||||
int size2 = sizeof(arr2) / sizeof(arr2[0]);
|
||||
int Dog1, Dog2;
|
||||
|
||||
Dog = FindOneSingleDog(arr1, size1);
|
||||
|
||||
FindTwoSingleDog(arr2, size2, &Dog1, &Dog2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
51
code/Exercises/advanced C/test/02/04 两数平均值/main.c
Normal file
51
code/Exercises/advanced C/test/02/04 两数平均值/main.c
Normal file
@@ -0,0 +1,51 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
// 今天在听课的时候听到一个老师讲 平均数求法,一时之间我竟无从下手,其实这个问题很早之前我们老师就说过了。
|
||||
// 虽然这种题确实是一种考验计算机专业的人的专业度的问题,但是大多老师讲完之后恐怕根本也不会去给你布置
|
||||
// 练习来强化你的记忆,所以你可能会很快忘记这种特殊的解法(也许是我太卑微了,大家都过目不忘??)。
|
||||
//所以,大多时候,讲到这个的老师无非是为了炫技,让你觉得他很专业。
|
||||
|
||||
// 但是我忘记了是真的,我没能想出来也是真的,我真菜啊,所以,我决定动手写一下
|
||||
|
||||
#include<stdio.h>
|
||||
|
||||
int main(void) {
|
||||
|
||||
int a = 10;
|
||||
int b = 11;
|
||||
double aver;
|
||||
int Aver;
|
||||
|
||||
// 方法1:直接求和除以2 这种方法的问题是:如果 a + b 会超过 int 最大能表示的范围,那结果就不是你想要的了
|
||||
|
||||
|
||||
|
||||
// 方法2:
|
||||
|
||||
// 确定两个的大小
|
||||
int max = a > b ? a : b;
|
||||
int min = a < b ? a : b;
|
||||
|
||||
// 平均值等于:大 - 后面这一坨 或者 小 + 后面这一坨
|
||||
aver = max - (max - min) / 2.0;
|
||||
|
||||
// 比较僵硬的是,整数的平均值要用浮点数来表示,不然可能会丧失精度
|
||||
printf("%.2f\n", aver);
|
||||
|
||||
|
||||
|
||||
// 方法3:这个方法可以说就是炫技的地方所在了,但是位运算确实效率是比较高的。
|
||||
|
||||
Aver = (a & b) + ((a ^ b) >> 1);
|
||||
printf("%d\n", Aver);
|
||||
|
||||
// a & b 得到的是 a,b 相同二进制位所表示的数
|
||||
// 比如 2 和 2 相同,那他们的平均值就是 2
|
||||
// a ^ b 表示 a 异或 b,得到的是 a,b 不同二进制位的和表示的数,除以 2,就是这部分两数的平均值。
|
||||
// 其实这个思想挺像 方法2 的思想,就将数分为两部分,依靠 2 进制的性质进行计算。
|
||||
// 位运算会向下取整,如果要求平均值有小数部分,那这个方法也就僵硬了
|
||||
// 参考博客:https://blog.csdn.net/weixin_43214609/article/details/83547313?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
// 字符串的左旋问题包含两点:
|
||||
// 1. 左旋字符串
|
||||
// 2. 判断字符串是否是另一个字符串左旋得到的
|
||||
// 这次我们一起处理这两个问题。
|
||||
|
||||
#include<stdio.h>
|
||||
#include<string.h>
|
||||
|
||||
// 让字符串左旋,我们可以先实现一个函数左旋一个字符,然后用另一个函数去调用这个函数,实现旋转多个字符
|
||||
|
||||
void LeftRotateOne(char str[], int size) {
|
||||
|
||||
char tmp;
|
||||
int i;
|
||||
|
||||
if (str == NULL)
|
||||
return;
|
||||
|
||||
if (size == 1)
|
||||
return;
|
||||
|
||||
// 先备份字符串第一个元素
|
||||
tmp = str[0];
|
||||
|
||||
// 将字符串从第二个元素开始依次向前移动一位
|
||||
for (i = 1; i < size; i++) {
|
||||
str[i - 1] = str[i];
|
||||
}
|
||||
|
||||
str[size - 1] = tmp;
|
||||
|
||||
}
|
||||
|
||||
// 这样的算法,如果 size 比较大,效率是很低的,因为数组每次移动复杂度都是 O(n)
|
||||
void LeftRotateN(char str[], int size, int n) {
|
||||
|
||||
if (str == NULL)
|
||||
return;
|
||||
|
||||
// 求模是因为:比如 “ABCD” 左旋 4 次 等于没左旋
|
||||
for (int i = 0; i < n % size; i++) {
|
||||
LeftRotateOne(str, size);
|
||||
}
|
||||
}
|
||||
// 判断 str1 str2 是否互为左旋
|
||||
int LeftRotateCmp(char str1[], char str2[]) {
|
||||
|
||||
int len1 = strlen(str1);
|
||||
int len2 = strlen(str2);
|
||||
|
||||
if (str1 == NULL || str2 == NULL)
|
||||
return -1;
|
||||
|
||||
if (len1 != len2)
|
||||
return 0;
|
||||
|
||||
// 将 str1 用 for 循环左旋 len1 次,每次左旋后与 str2 比较
|
||||
// 其实最多只用循环 len1 - 1 次即可,但是如果 写成 i < len1 - 1
|
||||
// 最后依次旋转的结果不会被比较!!而且即使多旋转了一次,相当于判断结束将 str1 归位了,一举两得!
|
||||
for (int i = 0; i < len1; i++) {
|
||||
if (strcmp(str1, str2) == 0) {
|
||||
return 1;
|
||||
}
|
||||
LeftRotateOne(str1, len1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
||||
char str[] = "ABCD";
|
||||
char str1[] = "DABC";
|
||||
|
||||
//LeftRotateOne(str, strlen(str));
|
||||
//LeftRotateN(str, strlen(str), 4);
|
||||
int a = LeftRotateCmp(str, str1);
|
||||
|
||||
printf("%d", a);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
3
code/Exercises/advanced C/test/02/05 字符串左旋问题/readme.md
Normal file
3
code/Exercises/advanced C/test/02/05 字符串左旋问题/readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 字符串左旋
|
||||
|
||||
这个问题我们在【习题一】也就是 上一级 【01】目录中讨论过,此次给出另一个中解法
|
||||
Reference in New Issue
Block a user