学习C语言3-深究数组(上)
一,数组是什么?
数组又分为一维数组和二维数组。
无论是一维数组还是二维数组,都是一组相同数据类型的有序集合,它能将一系列相同类型的数据看作一个整体,使用一个名字命名,再用下标进行分量标识,在内存中连续存放,用数组名和下标可以唯一地确定数组元素。
分别给大家看看最基本的定义和初始化问题
1.一维数组
数据类型 数组名[数组长度]; char str[100]; char str[100] = { 1,2,3 }; char str[100] = { 'a','b','c' }; char str[100] = "abc"; char str[]= { 'a','b','c' };//当把数据全部给出时,可以省略数组长度
还要提一下的:
这是为什么?这么小的区别,为什么输出会不一样?
我把这个语句屏蔽掉也没啥用。
这就是一个基础概念的问题,在定义数组时初始化,初始化是对整个数组进行操作,而后面那种方式是对a[10]进行赋值,但是数组长度为10,最大下标为9,所以必须要加{},不然就会报错。
加了花括号我也不知道有啥用。。。。a数组中就没有第十一个元素。
那么,邪恶的事情又来了。
由上面三张图我们可以知道
1)数组未初始化,数组里面的值都是随机值。
2)数组初始化为{0},数组里面的值都是0。
3)数组初始化为{非零值},数组里面第一个值是非零值,其他的值都是0。
2.二维数组
数据类型 数组名[行下标][列下标];
数据类型 数组名[行下标][列下标]; char a[100][100]; char a[3][4] = { 1,2,3,4,5,6,7,8,9,0,11,12 }; char a[3][4] = { {1,2,3,4},{5,6,7,8},{9,0,11,12} }; //可以在里面加花括号也可以不加,都是一样的 二维数组省略长度时,只能省略行下标的书写,这是规定
二维数组是不是也有这种邪恶的事呢?
嘿嘿嘿,有的呢!
这样的话是不是会方便很多呢?嘿嘿嘿,不要说我为啥还有一些基础的东西不讲,我们都开始深究了,太基础的东西我只说重要一些的。
这也是一种比较常见的初始化二维数组的方法。
让我们接下来再看看字符串数组的初始化,因为字符串有一个专门的输入函数gets,输入一整行的字符串,所以可以只用一个for循环来完成初始化。
每次输入一行后,输入一个回车键,跳出当前的for循环,进行下一次,可以少写,不能多写,多的部分会被舍弃。
现在来讲一些其他细节的东西
1.printf,scanf与puts,gets
scanf遇到空格或回车就结束
gets遇到回车才结束。
scanf("%d",&arr);//一般scanf后面都是要加&符号的,如果是写到数组里面,加不加&都无所谓。 sacnf("%s",str); gets(str)//直接用数组名即可
2.“a”与‘a’的区别
“a”占两个字节,因为”“是字符串标志,系统会自动给字符串的结尾加上一个结束标识符'\0',所以这个占了两个字节
‘a’只占一个字节,因为‘’是字符标志,只有a一个字符,只占一个字节。
3.数组名是一个地址常量,存放数组内存空间的首地址,如果是&数组名,取出的就是整个数组的地址,当然,这两个的值是没有什么区别的,但是在一定的情况下就会出现区别。C语言中规定,只能引用单个的数组元素,而不能一次引用整个数组。
数组名加1的含义是往后走一个元素的大小,&数组名+1的含义就是往后走一个数组的长度。
4.下标从0开始,下标不能越界。因为,一旦发生下标越界,就会把数据写到其他变量所占的存储单元中,甚至写入程序代码段,有可能造成不可预料的后果。
5.虽然C语言规定,只有静态存储的数组才能初始化,但一般的C语言编译系统都允许对动态存储的数组赋初值。如果静态存储的数组没有初始化,系统将自动给所有的数组元素赋值为0;
6.使用数组时,一般使用宏定义。
宏定义有宏定义的优势,修改更方便,只需要在define后面修改就好;
7.C语言支持多维数组,最常见的多维数组是二维数组。
二,话不多说,咱来上题。
一,基础一二维数组问题
//输出大于平均值的数 #include<stdio.h> int main(){ int i,n; double ave,sum; int a[10]; printf("请输入n:"); scanf("%d",&n); if(n>=1&&n<=10){ printf("请输入%d个数据:",n); for(i=0;i<n;i++){ scanf("%d",&a[i]);//需要一个循环语句来输入数组的数据 } sum=0; for(i=0;i<n;i++){ sum+=a[i];//同样也需要一个循环语句来累加 } ave=sum/n; printf("ave=%.2f\n",ave); printf(">ave"); for(i=0;i<n;i++){//数组中的for循环不能用等于号哦,用等于号就需要多输出一个数据,因为数组从零开始 if(a[i]>ave) printf("%d ",a[i]); } printf("\n"); } else printf("Invlid Value.\n"); return 0; }
#include<stdio.h> #define MAXN 46//定义在最上面,需要更改时便只需改这一个地方。 数组都可以如此,更方便 int main(){ int i,n; int fib[MAXN]={1,1};//先输出前两项 printf("请输入n:"); scanf("%d",&n); if(n>=1&&n<=46){ for(i=2;i<n;i++){//i=2,是因为,数组从零开始,i=2时,其实是第三项 fib[i]=fib[i-1]+fib[i-2];//前一项等于后两项之和 } for(i=0;i<n;i++){ printf("%6d",fib[i]); if((i+1)%5==0) printf("\n"); } if(n%5!=0) printf("\n"); } else printf("Invalid Value!\n"); return 0; }
#include<stdio.h> #define MAXN 8 int main(){ int i,n,response; int count[MAXN+1];//实际问题是以1开头的,所以定义一个更长的,其实也可以在宏定义时定义更长的 printf("请输入n:"); scanf("%d",&n); for(i=1;i<=MAXN;i++) count[i]=0;//数据的初始化,避免该存储单元之前的数据影响 !!!!!!! for(i=1;i<=n;i++){ printf("Enter your response:"); scanf("%d",&response); if(response>=1&&response<=MAXN) count[response]++;//等价于count[response]+=1. 上面已经初始化过了,所以出现一次就会累加一次 else printf("Invalid:%d\n",response); } printf("result:\n"); for(i=1;i<=MAXN;i++){ if(count[i]!=0) printf("%4d%4d\n",i,count[i]); } return 0; }
这个题要看看!!!
#include<stdio.h> #define MAXN 6 #define MAXM 6 int main(){ int col,i,j,m,n,row; int a[MAXM][MAXN]; printf("请输入m,n:"); scanf("%d%d",&m,&n); printf("请输入%d个数:\n",m*n); for(i=0;i<m;i++){ for(j=0;j<n;j++){ scanf("%d",&a[i][j]); } }//二维数组的初始化方式,双重for循环 row=col=0; for(i=0;i<m;i++){//遍历数组找出最大值 for(j=0;j<n;j++){ if(a[i][j]>a[row][col]){ row=i; col=j; } } } printf("max=a[%d][%d]=%d\n",row,col,a[row][col]); return 0; }
把上面这几个东西搞熟,基本的一二维数组问题就都可以解决。
二,数组的深入用法
数组的作用当然不止这些,数组的优越性在于元素众多,及它的可选择性,因为元素众多,所以你可以选择多种元素。这个在后面尤为重要,(所以咱后面再讲,嘿嘿嘿)。
我们这里主要来讲一讲其他的两个主要作用,排序与查找。
上题目:
1.顺序查找法
#include<stdio.h> #define MAXN 10 int main(){ int i,flag,n,x; int a[MAXN]; printf("please input n,x:"); scanf("%d%d",&n,&x); printf("请输入%d个数:",n); for(i=0;i<n;i++){ scanf("%d",&a[i]);//向数组内添加数据 } flag=0; for(i=0;i<n;i++){ if(a[i]==x){ printf("Index is %d\n",i); flag=1; } } if(flag==0) printf("Not found!\n"); return 0; }
2.
#include<stdio.h> #define MAXN 10 int main(){ int i,index,n; int a[MAXN]; printf("Enten n:"); scanf("%d",&n); printf("请输入%d个数:",n); for(i=0;i<n;i++) scanf("%d",&a[i]);//数组输入数据,借助一个循环。 index=0;//假设index(首地址)为最小的项的下标 for(i=0;i<n;i++){ if(a[i]<a[index]) index=i;//如果有比他更小的,就换到首地址去 } printf("min is %d\tsub is %d\n",a[index],index); return 0; }
3.a与b中找相同(重点看题4)
#include<stdio.h> int main(){ int a[]={1,2,3,4,5},b[]={3,4,5,6,7,8},c[6]; int i,j,k=0; for(i=0;i<5;i++){ for(j=0;j<6;j++){ if(a[i]==b[j]){ c[k]=a[i]; k++; break; } } } for(i=0;i<k;i++){ printf("%d ",c[i]); } return 0; }
4.a与b中找不同
#include<stdio.h> #define N 10 int main(){ int m,n,i,j,k,a[N],b[N],c[2*N],flag=0; printf("请输入一个数:"); scanf("%d",&m); printf("请输入%d个数据:",m); for(i=0;i<m;i++){ scanf("%d",&a[i]);//初始化a数组 } printf("请输入一个数:"); scanf("%d",&n); printf("请输入%d个数据:",n); for(i=0;i<n;i++){ scanf("%d",&b[i]);//初始化b数组 } for(i=0;i<m;i++){ for(j=0;j<n;j++){ if(a[i]==b[j]) break;//将a数组中的元素拿出与b数组中的元素比较 } if(j>=n){ flag=1; c[k++]=a[i];//如果是正常循环结束,则表明a中的元素无与b中相同的元素 ,且flag=1 } } for(j=0;j<n;j++){ for(i=0;i<m;i++){ if(b[j]==a[i]) break; } if(i>=m){ flag=1; c[k++]=b[j];//b也要与a比较 } } for(i=0;i<k;i++) printf("%d ",c[i]); if(flag==0) printf("No found!");//避免两数组无非公有元素而无操作 return 0; }
5.最大最小值交换位置
#include<stdio.h> #define N 10 int main(){ int n,i,t,a[N],max,min; printf("请输入一个数:"); scanf("%d",&n); printf("请输入%d个数:",n); for(i=0;i<n;i++) scanf("%d",&a[i]); max=0; min=0;//初始化最大最小值,使第一项为最大最小值。 for(i=0;i<n;i++){ if(a[max]<a[i]) max=i; if(a[min]>a[i]) min=i; } t=a[max]; a[max]=a[n-1]; a[n-1]=t; //赋值过程一定要放在for循环外,循环结束了以后才能知道最大最小,切记切记 t=a[min]; a[min]=a[0]; a[0]=t; printf("%d %d",a[0],a[n-1]); return 0; }
6.选择法排序
#include<stdio.h> #define MAXN 10 int main(){ int i,index,k,n,temp; int a[MAXN]; printf("请输入n:"); scanf("%d",&n); printf("请输入%d个数:",n); for(i=0;i<n;i++) scanf("%d",&a[i]); for(k=0;k<n-1;k++){ index=k; for(i=k+1;i<n;i++){ if(a[i]<a[index]) index=i; } temp=a[index]; a[index]=a[k]; a[k]=temp; }//k最大也只是n-2,i最大为n-1,即将a[k]与a[i]比较,在for循环结束后,下标已经交换完成,再交换数据 printf("排序为:"); for(i=0;i<n;i++) printf("%d ",a[i]); printf("\n"); return 0; } //第1轮在待排序记录r[1]-r[n]中选出最小的记录,将它与r[1]交换;第2轮在待排序记录r[2]-r[n]中选出最小的记录,将它与r[2]交换;以此类推,第i趟在待排序记录r[i]~r[n]中选出最小的记录,将它与r[i]交换,使有序序列不断增长直到全部排序完毕。
7.二分查找法
#include<stdio.h> int main(){ int low,high,mid,n=10,x; int a[10]={1,2,3,4,5,6,7,8,9,10}; printf("请输入x:"); scanf("%d",&x); low=0; high=n-1; while(low<=high){ mid=(high+low)/2; if(x==a[mid]) break; else if(x<a[mid]) high=mid-1; else low=mid+1; }//这个循环就是二分查找法的主体部分。 if(low<=high) printf("Index is %d\n",mid); else printf("Noy Found"); return 0; }
8.冒泡排序
#include<stdio.h> #define MAXN 10 void swap(int *px,int *py); void bubble(int a[],int n); int main(){ int n,a[MAXN],i; printf("Enter n(n<=10):"); scanf("%d",&n); printf("Enter %d integers:",n); for(i=0;i<n;i++) scanf("&d",&a[i]); bubble(a,n);//用数组名即可。 printf("After sorted:"); for(i=0;i<n;i++) printf("%3d",a[i]); return 0; } void bubble(int a[],int n){ int i,j,t; for(i=1;i<n;i++){ for(j=0;j<n-i;j++){ if(a[j]>a[j+1]) swap(&a[j],&a[j+1]); } } } void swap(int *px,int *py){ int t; t=*px; *px=*py; *py=t; }
***9.判断回文-数组-递归-指针三种方法
1)数组
#include<stdio.h> #define MAXLINE 80 int main(){ int i,k; char line[MAXLINE]; //输入字符串 printf("Enter a string:"); //输入字符串 k=0; while((line[k]=getchar())!='\n') k++; line[k]='\0';//必须要人为加上‘\0’. //分别指向第一个和最后一个元素位置。 i=0; k-=1; while(i<k){ if(line[i]!=line[k])//若对应字符不相等,则提前结束循环。 break; i++; k--; } //判断while循环是否正常结束,若是则说明字符串是回文。 if(i>=k) printf("是回文!"); else printf("不是回文!"); return 0; }
2)递归
//递归 #include<stdio.h> #include<string.h> int judge(int low,int high,char *arr,int len){ //最终条件。 if(len==0 || len==1) return 1; if(arr[low]!=arr[high]) return 0; return judge(low+1,high-1,arr,len-2); } int main(){ char arr[10]="aaabbaaa"; int len=strlen(arr); if(judge(0,len-1,arr,len)) printf("是!"); else printf("不是!"); return 0; } /*递归的作用在于把问题的规模不断缩少,直到问题缩少到简单地解决 通过观察可以知道,一个回文字符串其中内部也是回文。所以,我们只需要以去掉两端的字符的形式一层层检查, 每一次的检查都去掉了两个字符,这样就达到了缩少问题规模的目的。 1. 字符串长度可能会奇数或偶数: 如果字符串长度是奇数,字符串会剩下最中间那位字符,但其不影响回文。当检查到长度为1的时候即代表此字符串是回文 如果字符串长度是偶数,当两端的字符串两两比较检查后不会剩下字符。即检查到长度为0的时候即代表此字符串是回文 2. 如果检查到两端两个字符不相同。则说明此字符串不是回文,直接返回0,不需要继续检查
3)指针与数组方法差不多。自己理解吧。
10.方阵转置
#include<stdio.h> #define MAXN 6 int main(){ int i,j,n,temp; int a[MAXN][MAXN]; printf("ENTER n:"); scanf("%d",&n); for(i=0;i<n;i++){ for(j=0;j<n;j++){ a[i][j]=i*n+j+1;//给数组元素赋值 } } for(i=0;i<n;i++){ for(j=0;j<n;j++){ if(i<=j){ temp=a[i][j]; a[i][j]=a[j][i]; a[j][i]=temp; } } } for(i=0;i<n;i++){ for(j=0;j<n;j++){ printf("%4d",a[i][j]); } printf("\n"); } return 0; }
三,小细节
1.二维数组的每一行都可看做一个一维数组,可直接用数组名+[行号]代表该行首元素地址。
2.数组要重视长度,防止越界。
3.二维数组其实就是一种特殊的一维数组,二维数组存储方式就是类似一维数组的。
4.二维数组的数组名指的是数组的首地址,即第一行的地址。
5.数组名不能做自增自减运算,数组名是地址常量,不能做此类运算。
请大家务必要把题目弄懂弄透!
希望大家能学到东西,也静待大家斧正和指教!!!