零基础学浙大翁恺C语言(7):数组运算

零基础学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;
}

前面改进求素数的方法,改进速度,但基本思路都是,拿一个数出来整除,只不过是数越来越少。

构造素数表

接下来介绍另一种方法:不判断这个数是不是素数,而是构造一张素数表,留下的全都是素数。

  1. 令x为2
  2. 将2x、3x、4x直至ax<n的数标记为非素数
  3. 令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],那么交换保留最大的数。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值