3.1 数组
发布日期:2021-05-04 20:16:36 浏览次数:21 分类:原创文章

本文共 4716 字,大约阅读时间需要 15 分钟。

3.1 数组

本章主要是介绍数组和字符串,两个都可以用来保存大量的数据。其中字符串可以视作一种特殊的字符数组,并且由于其特殊性,会介绍一些比较实用的方法和工具


数组的定义

“数组是指有序的元素序列。如果将有限个类型相同的变量的集合命名,那么这个名称就是数组名,而组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。”

上面这段话引自 (标记出来的是我觉得比较重要的要点)

通常,我们在c++中用这样的方式定义一个一维数组(通常!毕竟我比较fw)

#inlcude<stdio.h>int main(){   	数组里面的变量类型名+空格+[最多能够存储多少个该类型]	java中[]内可以不加数字表示没有固定数组的长度	但gcc的编译器这么做会报错	但可以像下面这样定义:	char a[]="dhiuaslhf 32`14324";	int b[]={   1,2,3,4,5,6,7}}

这里我举一个例子:

#include<stdio.h>int main(){   	int a[10];} 

这里我定义了一个可以放10个int型变量的a数组。

我们可以通过以下的方式给我们的数组赋值:

#include<stdio.h>int main(){   	int a[10]={   1,2,3,4,5,6,7,8,9,10};} 

这个数组最多只能放10个int型的整数,如果放多了呢?

在这里插入图片描述
就会报出这样的错误

如果说我们只给数组的部分元素进行了赋值,那其他的元素的值会是多少呢?

在这里插入图片描述
其他几个元素的值是随机的(大概?反正不全是0就对了)

这里要注意的是对于a数组而言,第一个元素的下标是从0开始的,也就是说int a[10] 里面,包含的元素是a[0 - 9](懒得打字了,懂我意思就好2333

并且数组定义的大小是有范围的,因为调用的空间有限,有兴趣的可以尝试一下最大能定义到多少 (电脑:不,你没有兴趣

书上提到了一段代码

a[n++]=x;

事实上就是这两个代码合并起来

a[n]=x;n++;

至于优先级的问题,我搜了一下,++的优先度确实是比赋值要高的,所以也并不是很明白权当记忆了 (我是fw,如果有明白原理的麻烦告知以下)


memcpy 函数

使用这个函数首先是要包含头文件<string.h>的

格式是这样的:memcpy(存储目标内容的目标数组名,要复制的数据源,需要被复制的变量个数n)

注意到我这里没有加sizeof,事实上如果不加sizeof,这里默认的是两个字符串,如果需要进行更改,第三个参数位置这么写即可:sizeof(变量类型)*n (元素个数)。全部复制的话直接:sizeof(复制的数据源的数组名即可)

那如果要选择从哪里开始复制呢(不更改的话默认从头开始复制),需要更改第二个参数:要复制的数据源+开始复制的数组下标即可

选择从哪里存储被复制的元素的操作类似,更改第一个参数:存储目标内容的目标数组名+开始的下标

字符串(字符数组)的例子参考:

例子:
在这里插入图片描述


数组运用

还记得2.5排列的那道题目么,我们当时没有用数组的方法做,代码看起来非常的复杂,这里我们用数组重新再做一遍。

解决方案如下: (似乎也没有简单多少)

#include<stdio.h>#include<string.h>//用于调用memset int main(){   	int a[10];//这个数组分别用来存储1-9中每个数字出现的次数,例如a[3]里面放的就是3出现了几次 	for (int i=100;i<=333;i++){   			memset(a,0,sizeof(a));//将数组a中所有元素全部赋值为0 		//每次都要重新赋值一遍,防止对后面的循环造成影响		a[i%10]++;//i的个位十位百位		a[i%100/10]++;		a[i/100]++;		a[(2*i)%10]++;//2i的个位十位百位		a[(2*i)%100/10]++;		a[(2*i)/100]++;		a[(3*i)%10]++;//3i的个位十位百位		a[(3*i)%100/10]++;		a[(3*i)/100]++;		//现在a[1-9]分别放了其下标在这三个三位数中出现的次数		bool flag=true;//用于判断这个三位数是否符合条件 		for (int j=1;j<=9;j++){   			if (a[j]!=1)//如果1-9中的某个数不是出现一起且仅出现一次			{   				flag=false;//直接跳出循环 				break;			} 		}		if (flag)//如果经历上面的循环,flag还没有变为false			printf("%d %d %d\n",i,i*2,i*3); 	}	return 0;} 

这里运用了桶排序的思想

memset(a,0,sizeof(a))的作用是将a数组清零,它在string.h中定义。比起用for循环更加的快捷和方便

memset赋值赋值0就好了,不要想着把0的参数改了,去赋值其他的数字,事实上memset本身也是运用于字符串的工具,里面很多的用法和我们想的不是一样的,有兴趣的可以参考: 和


开灯问题

有 n 盏灯,编号为 1~n,第 1 个人把所有灯打开,第 2 个人按下所有编号为 2 的倍数的开关(这些灯将被关掉),第 3 个人按下所有编号为 3 的倍数的开关(其中关掉的灯将被打开,开着的灯将被关闭),依此类推。一共有 k 个人,问最后有哪些灯开着?输入:n 和 k,输出开着的灯编号。k≤n≤1000

样例输入: 7 3

样例输出: 1 5 6 7

分析:我们可以将这n盏灯看作数组里的n个元素,开始的时候都是关着的,我们可以将关着的这个状态定义为0 (bool 里面的 false),那么开灯就变为1。在模拟完整个过程后,遍历一遍下标为1-n的所有元素,元素值为1即为开着的灯,进行输出。

代码如下:

#include<stdio.h>#include<string.h>//用于调用memset int main(){   	int a[1011],n,k;	memset(a,0,sizeof(a)); 	scanf("%d%d",&n,&k);	for (int i=1;i<=k;i++){   //一共k个人,进行k轮 		for (int j=i;j<=n;j+=i) //这样做的原因可以减少循环和判断的次数,直接找到1-n中被i整除的数			a[j]=1-a[j];//0-->1,1-->0; 	}	for (int i=1;i<=n;i++)		if (a[i]) printf("%d ",i);//输出下标,即为处于开灯状态(1)的灯 	return 0;} 

书本上first的定义是用于规范格式的,因为有的oj对输出格式要求非常高,由于我没交oj,所以没有做这方面的处理


蛇形填数

给定一个 n , 在 n * n 的方阵中填入 1 ,2, 3,……,n * n, 要求填成蛇形。

例如在 n = 5 时 , 如下所示:

13 14 15 16 1

12 23 24 17 2

11 22 25 18 3

10 21 20 19 4

9 8 7 6 5

(markdown编辑器用的不是很熟T_T)

分析:我拿到这道题的时候乍一看想用dfs做,分析了一下,发现它填数的顺序还是比较有规律的。我们这里考虑使用二维数组,首先定义左上角为1,然后先往下,再往左,再往上,再往右进行填数。那么怎么判断什么时候开始转弯呢,在这个方阵的周围围上一圈障碍物即可。

首先什么叫做二维数组呢?
二维数组本质上是以数组作为数组元素的数组,即“数组的数组”。

这样子是不是感觉非常复杂?说的更加通俗一点。就是本来的一维数组里面的每一个元素,我们定义为一个数组。二维数组中就含有多个的一维数组。

事实上,二维数组通常以矩阵和方阵的形式表现出来,a[m][n],其中m即为行,n为列。(关系型数据库)

代码如下:

#include<stdio.h>#include<string.h>int a[30][30];//定义在main函数外的我们称为全局变量,全局变量是可以被本程序所有对象或函数引用int main(){   	int n,x,y,tot=0;	scanf("%d",&n);	//x,y 为进行填数的格子的坐标	memset(a,0,sizeof(a));//将数组所有的元素赋值为0,为0的元素即为可以进行赋值(填数)的元素	for (int i=0;i<=n+1;i++){   //设置障碍		a[i][0]=1;		a[i][n+1]=1;		a[0][i]=1;		a[n+1][i+1]=1;	}}

代码写到这里,我们先输出来看一下:
在这里插入图片描述
n* n的方阵中,0的位置都是可以进行填数,那为什么我输出了n+1* n+1呢?事实上周围外面的一圈即做“障碍物”的作用,保证在填数的过程中不会,不会填到n*n的范围之外。

(我写完代码之后在这里想了一下,感觉似乎这圈“障碍物”不是完全必要的,但是为了保险起见,我还是预定义了一下。毕竟我是fw本废,T0T)

代码如下:

#include<stdio.h>#include<string.h>int a[30][30];//定义在main函数外的我们称为全局变量,全局变量是可以被本程序所有对象或函数引用int main(){   	int n,x,y,tot=0;	scanf("%d",&n);	//x,y 为进行填数的格子的坐标	memset(a,0,sizeof(a));//将数组所有的元素赋值为0,为0的元素即为可以进行赋值(填数)的元素	for (int i=0;i<=n+1;i++){   //设置障碍		a[i][0]=1;		a[i][n+1]=1;		a[0][i]=1;		a[n+1][i+1]=1;	}	x=1;	y=n;//预定左上角的位置为开始填数的位置	a[x][y]=1;	tot=1; 	while (tot<n*n){   //填n*n个数 		while (a[x+1][y]==0){   //向下为可填的数则向下走 			tot++;			a[x+1][y]=tot;			x++; 		}		while (a[x][y-1]==0){   //向左为可填的数向左走 			tot++;			a[x][y-1]=tot;			y--;		}		while (a[x-1][y]==0){   //向上为可填的数向上走 			tot++;			a[x-1][y]=tot;			x--;		}		while (a[x][y+1]==0){   //向右为可填的数向右走 			tot++;			a[x][y+1]=tot;			y++;		}		//一圈做完进行下一轮,直到数被全部填完 	} 	for (int i=1;i<=n;i++){   		for (int j=1;j<=n;j++)			printf("%6d",a[i][j]);		printf("\n");	}}

书上的代码非常的简洁,因为他将很多++还有赋值的代码放到了一起,还使用了! 的定义。但是正如书上所说,利用C语言简洁的语法的前提是,代码的准确性,和可读性。所以还是建议按照自己的习惯来嘛。

上一篇:吴恩达深度学习 C1_W2
下一篇:吴恩达深度学习 C1_W1

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年03月28日 15时28分59秒