零基础学C语言(7):数组运算
在一组给定的数据中,如何找出某个数据是否存在?
数组的集成初始化
int a[] = {2,4,6,7,1,3,5,9,11,13,23,14,32};
- 直接用大括号给出数组的所有元素的初始值。
- 不需要给出数组的大小,编译器替你数数
int a[5] = {2};
是{2,0,0,0,0},第一位是2,其余补0。
集成初始化时的定位(只有C99能用)
int a[10] = {[0]=2,[2]=3,6,};
- 用[n]在初始化数据中给出定位
- 没有定位的数据接在前面的位置后面
- 其他位置的值补零
- 也可以不给出数组大小,让编译器算
- 特别适合初始数据稀疏的数组
#include <stdio.h>
int main()
{
int a[10] = {[0]=2,[2]=3,6,};
{
int i;
for (i=0;i<10;i++){
printf("%d\t",a[i]);
}
printf("\n");
}
return 0;
}
输出
2 0 3 6 0 0 0 0 0 0
数组的大小
sizeof
给出整个数组所占据的内容的大小,单位是字节sizeof(a[O])
给出数组中单个元素的大小,于是相除sizeof(a)/sizeof(a[0])
就得到了数组的单元个数- 这样的代码,一旦修改数组中初始的数据,不需要修改遍历的代码。
#include <stdio.h>
int main()
{
int a[] = {2,4,6,7,1,3,5,8,11,13,23,};
{
int i;
printf("%lu\n",sizeof(a));
printf("%lu\n",sizeof(a[0]));
for (i=0;i<sizeof(a)/sizeof(a[0]);i++){
printf("%d ",a[i]);
}
printf("\n");
}
return 0;
}
44 //sizeof(a)) 数组a里总共44个字节
4 //sizeof(a[0]) 数组a里第一个元素占4个字节
2 4 6 7 1 3 5 8 11 13 23
数组的赋值
数组变量是特殊的,不能把数组a赋值给b[]:
int a[]={1,2,3,5,4};
int b[]=a;
要把一个数组的所有元素交给另一个数组,必须采用遍历:
for ( i=0; i<length;i++){
b[i]=a[i];
}
数组例子:素数
#include <stdio.h>
int isPrime(int x);
int main(void)
{
int x;
scanf("%d",&x);
if ( isPrime (x) ){
printf("%d是素数\n",x);
} else{
printf("%d不是素数\n",x);
}
return 0;
}
定义的函数:从2到x-1测试是否可以整除,做n-1次。(当n很大时就是n遍)
int isPrime(int x)
{
int ret = 1;
int i;
if (x==1) ret = 0 ;
for (i=2;i<x;i++){
if(x%i==0){
ret=0;
break;
}
}
return ret;
}
有人说,所有能整除2的都不是素数,那么去掉偶数后,从3到x-1,每次加2,测试能否整除。(如果x是偶数,立刻,否则要循环1+(n-3)/2遍,当n很大时就是n/2遍)
int isPrime(int x)
{
int ret = 1;
int i;
if (x==1 ||
(x%2==0 && x!=2))
ret = 0 ;
for (i=3;i<x;i+=2){
if(x%i==0){
ret=0;
break;
}
}
return ret;
}
无须到x-1,到sqrt(x)就够了。
int isPrime(int x)
{
int ret = 1;
int i;
if (x==1 ||
(x%2==0 && x!=2))
ret = 0 ;
for (i=3;i<sqrt(x);i+=2){
if(x%i==0){
ret=0;
break;
}
}
return ret;
}
更好的:拿比x小的素数来整除,但需要前提有这么一张素数表。
#include <stdio.h>
int main(void)
{
const int number = 100;
int prime[number] = {2}; //2是第一个素数
int count = 1;
int i = 3;
while ( count < number ){
if( isPrime(i,prime,count) ){
prime[count++] = i; //先把3放到cnt1,然后把所指位置移到cnt2
}//常用套路:用cnt++表示写进去的位置,然后写进去,然后移一位。
i++;
}
for (i = 0; i<number; i++){
printf("%d",prime[i]);
if( (i+1)%5 ) printf("\t");
else printf("\n");
}
return 0;
}
int isPrime(int x, int knownPrimes[], int numberOfKnownPrimes)
{
int ret = 1;
int i;
for ( i=0; i< numberOfKnownPrimes; i++) {
if(x%knownPrimes[i]==0){
ret=0;
break;
}
}
return ret;
}
#include <stdio.h>
int isPrime(int x, int knownPrimes[], int numberOfKnownPrimes);
int main(void)
{
const int number = 10;
int prime[number] = {2}; //2是第一个素数
int count = 1;
int i = 3;
//为了好看,加一串表头,0-9,表示放进prime的位置。
{
int i;
printf("\t\t\t\t");
for (i=0;i<number;i++){
printf("%d\t",i);
}
printf("\n");
}
while ( count < number ){
if( isPrime(i,prime,count) ){
prime[count++] = i; //先把3放到cnt1,然后把所指位置移到cnt2
}//常用套路:用cnt++表示写进去的位置,然后写进去,然后移一位。
//函数里加一段大括号调试 :
{//大括号里的i是自己的变量i,与前面无关。
printf("i=%d \tcnt=%d\t",i, count);//输出外面的i和count;
int i;
for (i=0; i<number; i++){
printf("%d\t",prime[i]);
}//遍历prime数组,
printf("\n");
}
i++;
}
for (i = 0; i<number; i++){
printf("%d",prime[i]);
if( (i+1)%5 ) printf("\t");
else printf("\n");
}
return 0;
}
int isPrime(int x, int knownPrimes[], int numberOfKnownPrimes)
{
int ret = 1;
int i;
for ( i=0; i< numberOfKnownPrimes; i++) {
if(x%knownPrimes[i]==0){
ret=0;
break;
}
}
return ret;
}
前面改进求素数的方法,改进速度,但基本思路都是,拿一个数出来整除,只不过是数越来越少。
构造素数表
接下来介绍另一种方法:不判断这个数是不是素数,而是构造一张素数表,留下的全都是素数。
- 令x为2
- 将2x、3x、4x直至ax<n的数标记为非素数
- 令x为下一个没有被标记为非素数的数,重复2;直到所有的数都已经尝试完毕
算法步骤(伪代码):
1.开辟prime[n],初始化其所有元素为1,prime[x]为1表示x是素数
2.令x=2
3.如果x是素数,则对于(i=2;xi<n;i++)令prime[ix]=O
4.令x++,如果x<n,重复3,否则结束。
#include <stdio.h>
int main()
{
const int maxNumber = 25;
int isPrime[maxNumber];
int i;
int x;
for ( i=0; i <maxNumber; i++){
isPrime[i] = i;
}
for (x=2;x<maxNumber;x++){
if (isPrime[x]){
for (i=2; i*x<maxNumber; i++){
isPrime[i*x]=0;
}
}
}
for (i=2;i<maxNumber; i++){
if( isPrime[i] ){
printf("%d\t",i);
}
}
printf("\n";)
return 0;
}
搜索
线性搜索
在一个数组中找到某个数的位置(或确认是否存在)。
遍历每一个数据。
#include <stdio.h>
//在数组a里头找key,同时需要一个参数len来表示数组a有多大
int search(int key, int a[], int len)
{
int ret = -1; //默认-1没找到,找到了就输出位置
for ( int i = 0; i<len; i++ )
{
if (key == a[i])
{
ret = i;
break;
}
}
return ret; //“单一出口”:用变量来表示找到的位置。
}
int main()
{
int a[] = {1,3,2,5,12,14,23,6,9,45};
int r = search(12,a,sizeof(a)/sizeof(a[0]));
printf("%d\n",r); //r是从0开始数的
return 0;
}
搜索的例子,美元数字对应英文单词
数据结构里的散列表hash表很适合做这个事情,value-key。
有两个数组,想要得到10对应的值。在数组1,第二个位置,然后输出数组2的第二个位置的值。
#include <stdio.h>
int amount[]={1,5,10,25,50};
char *name[]={"penny","nickel","dime","quarter","half-dollar"} ;
//search和前面的是一样的
int search(int key, int a[], int len)
{
int ret = -1; //默认-1没找到,找到了就输出位置
for ( int i = 0; i<len; i++ )
{
if (key == a[i])
{
ret = i;
break;
}
}
return ret; //“单一出口”:用变量来表示找到的位置。
}
int main()
{
int k =10; //用户输入的数字10
int r = search(10,amount,sizeof(amount)/sizeof(amount[0]));
if ( r>-1 )
{
printf("%s\n",name[r]); //输出name数组r位置处的字符串
}
return 0;
}
但这样是割裂的两个数组,这种程序结构对“cache’”是不友好的,希望把面额和名字放在一起。(python里的字典功能)
#include <stdio.h>
int amount[]={1,5,10,25,50};
char *name[]={"penny","nickel","dime","quarter","half-dollar"} ;
//用一个结构,制造coins的数组,每一项对应金额和面额
struct{
int amount;
char *name;
}coins[]={
{1,"penny"},
{5,"nickel"},
{10,"dime"},
{25,"quarter"},
{50,"half-dollar"},
};
//search和前面的是一样的
int search(int key, int a[], int len)
{
int ret = -1; //默认-1没找到,找到了就输出位置
for ( int i = 0; i<len; i++ )
{
if (key == a[i])
{
ret = i;
break;
}
}
return ret; //“单一出口”:用变量来表示找到的位置。
}
int main()
{
int k =10; //用户输入的数字10
//int r = search(k,amount,sizeof(amount)/sizeof(amount[0]));
for(int i=0;i<sizeof(coins)/sizeof(coins[0]);i++)
{
if(k == coins[i].amount){
printf("%s\n",coins[i].name);
break;
}
}
return 0;
}
线性搜索最大的问题是它的效率问题,从数组的第一个元素一直搜,如果不存在,就要全部搜索。运气好,第一个;运气不好,最后一个。
二分搜索
怎么做提高效率?
让要搜的数组是排好序的,比如从小到大,然后快速搜索。
写程序,得要有3个变量,left=0, right=len-1, 得要个while循环。
#include <stdio.h>
int search(int key, int a[], int len)
{
int ret =-1; //-1表示没找到
int left = 0;
int right = len-1;
while (left<right) //什么条件下需要继续找下去?
{
int mid =(left+right)/2;
if ( a[mid]==k )
{
ret = mid;
break ; //找到了要break
}else if ( a[mid]>k )
{
right=mid -1;
}else{
left=mid+1;
}
}
return ret;
}
二分搜索的搜索次数是log2n,也就是说有100个东西只需要搜索7次。1000的时候log2N是10次。
排序初步—选择排序
二分法效率很高,但如果数组是无序怎么办?那就要先对它进行排序。
1.先找到最大的数的位置。先把第0个数当做是最大的maxid,如果a[i]>a[maxid],那么交换保留最大的数。