IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 2021年11月15日到2021年11月21日笔记 -> 正文阅读

[数据结构与算法]2021年11月15日到2021年11月21日笔记

? ????????本周首先想到自己实现一下字符串函数,最终代码如下:

?????????strlen:

#include <stdio.h>
 int main(void)
 {		char str[100];
		scanf_s("%s", str, 100);
		int j = 0;
		while (str[j] != '\0') {
			++j;
		}
		printf("%d", j);
		 return 0;
 }

????????????????这个代码很显而易见的问题就是数组大小带来的安全性问题,可能会出现数组越界的情况,但我的目的只是想看一看这个函数是怎么实现的,所以并没有对此深究,但在实现这个函数时,我发现了一个VS2019上的特性:它的scanf函数输出字符串时,得带上字符数组的大小。

????????不然就会出现这样一个警告,最终经过查询才知道必须得带上数组的大小,这一点以后得注意。

strcmp?

#include<stdio.h>
int isSame(const char str[], const char str1[]);
int main(void) {
	char str[100], str1[100];
	scanf_s("%s %s", str, 100, str1, 100);
	printf("%d", isSame(str, str1));
	return 0;
}
int isSame(const char str[], const char str1[]) {
	int i = 0;
	while (1) {
		if (str[i] > str1[i]) {
				return 1;
			}
		else if (str[i] == str1[i]) {
			if (str[i] == '\0' && str1[i] == '\0') {
				return 0;
			}
			
		}
		else if(str[i] < str1[i]){
			return -1;
		}
		++i;
	}
}

????????利用一个函数大致实现了strcmp函数,但其中的while循环可能还能做优化,由于目的只是想了解这些函数的逻辑,所以这里并没有深究。

? ? ? ? 但在我看一些网课的时候,我发现有些UP主返回的是两个字符的ASCII码的差值,最终在? ? ? ?C Primer Plus上找到了答案:由于系统不同,别人可能是Linux,在Windows上,它只会返回1,0,-1;

strcat

????????写这个函数时遇到了一些小问题,刚开始的代码如下:

#include<stdio.h>
int main(void) {
	char str[100], str1[100];
	scanf_s("%s %s", str, 100, str1, 100);
	int i = 0, j = 0;
	while (str[i] != '\0') {
		++i;
	}
	while (str1[j] != '\0') {
		++j;
	}
	int x;
	for (x = 0; x < j; x++) {
		str[i + x] = str1[x];
	}
	printf("%s", str);
}

????????看似没啥问题,但运行之后的结果为:

?它出现了乱码,我发现少进行了一次迭代,少读入了'\0',最终代码如下:

#include<stdio.h>
int main(void) {
	char str[100], str1[100];
	scanf_s("%s %s", str, 100, str1, 100);
	int i = 0, j = 0;
	while (str[i] != '\0') {
		++i;
	}
	while (str1[j] != '\0') {
		++j;
	}
	int x;
	for (x = 0; x <= j; x++) {
		str[i + x] = str1[x];
	}
	printf("%s", str);
}

strcpy:

#include<stdio.h>
int main(void) {
	char str[50], str1[100];
	scanf_s("%s", str, 50);
	int i = 0;
	while (str[i] != '\0') {
		++i;
	}
	for (int j = 0; j <= i; j++) {
		str1[j] = str[j];
	}
	printf("复制后为:%s", str1);/*while (str[i] != '\0') {
	str1[i] = str[i];
	i++;
	}
	str1[i+1] = '\0';
	printf("复制后为:%s", str1);*///有一个问题是这样写为啥会多输出一个?呢,,,没理解
	}

? ? ? ? ? 对于多输出一个?的问题,在其他编译器上并不会出现这个问题,我认为这是编译器的问题

?????????至于strncpy等那些限定前几个字符的函数,原理基本相同,就不再写了。

????????本周还分析了汉诺塔问题,刚开始感觉比较抽象,不过最后也是看懂了,代码如下:

#include<stdio.h>
void hanoi(int, char, char, char);
void hanoi(int n, char x, char y, char z) {
	if (n == 1) {
		printf("%c-->%c\n", x, z);
	}
	else {
		hanoi(n - 1, x, z, y);
		printf("%c-->%c\n", x, z);
		hanoi(n - 1, y, x, z);
	}
}
int main(void) {
	int n;
	printf("请输入汉诺塔的层数:");
	scanf_s("%d", &n);
	hanoi(n, 'X', 'Y', 'Z');
}

?????????首先,这个问题采用递归的方法来处理是非常方便的,如果不使用递归,可能就需要一大堆代码才能实现。这个问题看似复杂难懂,但我们只需要知道我们的目的是什么这个问题也就不那么难处理了,我们可以将我们的思路分成几个步骤:

? ? ? ? 1.将前n-1个圆盘借助z移动到y;

? ? ? ? 2.将第n个圆盘移动到c;

????????3.将剩下的圆盘移动到z;

? ? ? ? 当然,这个问题的第三步也可以进行分步处理:

? ? ? ? 1.将前n-2个圆盘借助z移动到x;

? ? ? ? 2.将第n-1个元素移动到z;

? ? ? ? 而这些,我们只需要通过改变函数参数的顺序就能实现,所以,第三步我们可以直接写成:将剩下的圆盘借助x移动到z。由此我们就可以解决汉诺塔问题。

? ? ? ? 快速排序:

#include<stdio.h>
void quickSort(int array[], int left, int right) {
	int i = left, j = right;
	int temp;
	int pivot;
	pivot = array[(left + right) / 2];
	while (i <= j) {
		//从左到右找到大于等于基准点的元素
		while (array[i] < pivot) {
			i++;
		}
		while (array[j] > pivot) {
			//从右到左找到小于基准点的数
			j--;
		}
		//如果i小于等于j,则互换元素
		if (i <= j) {
			temp = array[i];
			array[i] = array[j];
			array[j] = temp;
			i++;
			j--;
		}
	}
	if (left < j) {
		quickSort(array, left, j);
	}
	if (i < right) {
		quickSort(array, i, right);
	}
}
int main(void) {
	int array[] = { 73,108,111,118,101,70,105,115,104,67,46,99,111,109 };
	int i, length;
	length = sizeof(array) / sizeof(array[0]);
	quickSort(array, 0, length - 1);

	printf("排序后的结果是:");
	for (i = 0; i < length; i++) {
		printf("%d ", array[i]);
	}
	putchar('\n'); 
	return 0;
}

? ? ? ? 这里大致分析一下快速排序的思路:它利用的是分治思想,首先找到一个基准点,将基准点以左都换成小于基准点的数,将基准点以右都换成大于基准点的数。最终在左右两边分别再找一个基准点,按同样的思路进行排序。最后形成从大到小排列。

? ? ? ? 高级宏定义

宏定义我们自然是非常熟悉,但高级宏定义听起来就比较nb了,接下来介绍两种:

1.宏定义是可以嵌套的,代码如下:

#include<stdio.h>
#define R 6371
#define PI 3.14
#define V PI * R * R * R * 4/3
int main(void) {
	printf("地球的体积为%.2f\n", V);
	return 0;
}

????????这里拿一个简单的例子进行举例,非常通俗易懂。

2.宏定义可以带参数:

#include<stdio.h>
#define MAX (x,y) ((x) > (y)? (x) : (y))
int main(void) {
	int x, y;
	printf("请输入两个整数:");
	scanf_s("%d %d", &x, &y);
	printf("%d是较大的那个数", MAX(x,y));

	return 0;
 }

????????我们也可以使用带参数的宏定义,但这样使用时我们要注意一个小细节,拿上面的代码举例:

????????宏定义时我们不能把MAX(x,y)写成MAX? ?(x,y),这样编译器不会认为你的宏定义带参数,因为它认为你所定义的是MAX,而不是MAX(x,y)。

? ? ? ? 但我们在进行宏定义的内容定义时,一定要带上括号,不然在我们要求比如x+1的宏定义时就会因为优先级的问题而出错!

? ? ? ? 另外,在我们使用高级宏定义想提高一些程序的效率,还有一些问题我们要注意:

? ? ? ? 先看这段代码:

#include<stdio.h>
#define SQUARE(x) ((x)*(x))
int main(void) {
	int i = 1;
	while (i <= 100) {
		printf("%d的平方是%d\n",i, SQUARE(i++));
	}
}

? ? ? ? 我们的目的是要打印1-100的平方值,我们这里使用了高级宏定义,但运行的结果却让人大跌眼镜:

出现了这种运行结果,仔细思考后,在进行宏定义运算时,运行一次就会进行两次自增运算,才出现了结果错误的现象,所以我们可以将代码改成这种形式

#include<stdio.h>
#define SQUARE(x) ((x)*(x))
int main(void) {
	int i = 1;
	while (i <= 100) {
		printf("%d的平方是%d\n",i, SQUARE(i));
		i++;
	}
}

?????????这样就解决了我们的问题。

? ? ? ? 结构体:

? ? ? ? 第一个结构体程序:

#include<stdio.h>

struct Book {
	char title[120];
	char author[40];
	float price;
	unsigned int date;
	char publisher[40];
};
int main(void) {
	struct Book book;
	printf("请输入书名:");
	scanf_s("%s", book.title, 120);
	printf("请输入作者:");
	scanf_s("%s", book.author, 40);
	printf("请输入售价:");
	scanf_s("%f", &book.price);
	printf("请输入出版日期:");
	scanf_s("%d", &book.date);
	printf("请输入出版社:");
	scanf_s("%s", book.publisher, 40);
	printf("\n=====数据录入完毕=====");
	printf("书名:%s\n", book.title);
	printf("作者:%s\n", book.author);
	printf("售价:%.2f\n", book.price);
	printf("出版日期:%d\n", book.date);
	printf("出版社:%s\n", book.publisher);
	return 0;
}

? ? ? ? 这是一个能说明结构体用来干什么的程序,将一组类型不同的、但是用来描述同一件事物的变量聚合在一起,这里引入了一个点运算符,点运算符在我们初始化变量时可以进行选择。

? ? ? ? 这里还有一个箭头运算符,在我i们访问结构体中的变量的时候也可进行选择:

#include<stdio.h>
int main(void) {
	struct Stu {
		char name[20];
		int age;
		double score;
	} s;
	s = {
		"张三",
		20,
		85.5
	};
	printf("%s %d %g\n", s.name, s.age, s.score);
	struct Stu* ps = &s;
	printf("%s %d %g\n", (*ps).name, (*ps).age, (*ps).score);
	printf("%s %d %g\n", ps->name, ps->age, ps->score);//结构体指针->成员变量名
}
#include<stdio.h>

int main(void) {
	
	struct A {
		char a;
		int b;
		char c;
	}a = { 'x',520, '0' };
	printf("size of a = %d\n", sizeof(a));
}

? ? ? ? 关于这个代码,在64操作系统下,按照我的想法,它输出的值应该是6,但实际的结果是12,

这是因为编译器有一个内存对齐操作所以就成为了4+4+4=12,每个char类型多填充了三个字节。

但我们将代码改成这种形式:

#include<stdio.h>

int main(void) {
	
	struct A {
		char a;
		char c;
		int b;
	}a = { 'x','0', 520 };
	printf("size of a = %d\n", sizeof(a));
}

????????它的值就变成了8,同样,用内存对齐我们也可以解释。

????????在我们进行内存对齐时,我们可以改变编译器的默认对齐数,

#pragma pack()

????????采用这种方式我们就可以修改我们的默认对齐数,当我们认为结构的对齐方式不合适时,我们可以更改默认对齐数。

????????我们也可以用宏来计算默认对齐数,代码如下:

#include<stdio.h>
#include<stddef.h>
#pragma pack(2)
struct S {
	char c1;
	int i;
	char c2;
};
#pragma pack()
int main(void) {
	printf("%d", offsetof(struct S, c1));
	printf("%d", offsetof(struct S, i));
	printf("%d", offsetof(struct S, c2));
}

????????采用这种方式我们可以计算我们的偏移量,offsetof在stddef.h头文件下。

? ? ? ? 结构体传参,下面的代码介绍结构体传参的方式:

#include<stdio.h>
struct S {
	int data[1000];
	int num;
};
struct S s = { {1, 2, 3, 4}, 1000 };
void print1(struct S s) {
	printf("%d\n", s.num);//传结构体
}
void print2(struct S* pts) {
	printf("%d\n", (*pts).num);//传地址
}
void print3(struct S* pts1) {
	printf("%d\n", pts1->num);//传地址
}
int main(void) {
	print1(s);
	print2(&s);
	print3(&s);
}

? ? ? ? 我们可以使用地址穿参,同时我们可以选择'->'与' . '两种方式,我们也可以直接传结构体。

直接传结构体采用的是拷贝的方式,这种方式相对于直接传地址来讲效率更低,所以直接传地址,采用指针是更高效的选择。

? ? ? ? 本周还进行了一些练习

#include<stdio.h>
#define N 10
int main(void) {
	int a[N] = { 0,1,2,3,4,5,6,7,8,9 };
	printf("原始数组为:");
	for (int i = 0; i < 10; i++) {
		printf("%d ", a[i]);
	}
	printf("\n");
	for (int i = 0; i < N / 2; i++) {
		int temp = a[i];
		a[i] = a[N - i - 1];
		a[N - i - 1] = temp;
	}
	for (int i = 0; i < 10; i++) {
		printf("%d ", a[i]);
	}//数组反向输出
}
#include<stdio.h>
int main(void) {
	int a[11] = { 1,4,6,9,13,16,19,28,40,100 }, num;
	printf("原始数组是:");
	for (int i = 0; i < 10; i++) {
		printf("%4d", a[i]);
	}
	printf("请输入你要插入到数组里的元素:");
	scanf_s("%d", &num);
	for (int i = 0; i < 11; i++) {
		if (a[i] > num) {
			int temp = a[i];
			a[i] = num;
			for (int x = i + 1; x < 11; x++) {
				int temp2 = a[x];
				a[x] = temp;
				temp = temp2;
			}
			break;
		}
	}
	for (int i = 0; i < 11; i++) {
		printf("%4d", a[i]);//数组中插入一个新元素
		}
}
#include<stdio.h>
int main(void) {
	int a[10];
	printf("请输入十个数字:");
	for (int i = 0; i < 10; i++) {
		scanf_s("%d", &a[i]);
	}
	for (int j = 0 ; j < 10; j++) {
		int min = j;
		for (int k = 9; k > j; k--) {
			if (a[min] > a[k]) {
				min = k;
			}
	}
		int temp = a[min];
		a[min] = a[j];
		a[j] = temp;
	}
	for (int i = 0; i < 10; i++) {
		printf("%d ", a[i]);//排序
	}
}

? ? ? ? 这就是本周的大致内容!

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-11-22 12:35:49  更:2021-11-22 12:37:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 12:48:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码