7.2-1 数组的运算
• 在一组给定的数据中,如何找出某个数据是否存在?
int main(void)
{
int a[] = {2,46,7,1,3,5,9,11,13,23,14,32};
int x;
int loc;
printf("请输入一个数字:");
scanf("%d", &x);
loc=search(x, a, sizeof(a[0]));
if ( loc != -1) {
printf("%d在%d个位置上\n", x, loc);
} else {
printf("%d不存在\n", x);
}
return 0;
}
int rearch(int key, int a[], int length)
{
int ret = -1;
int i;
for ( i=0; i< length; i++ ) {
if ( a[i] == key) {
ret = i;
break;
}
}
return ret;
}
数组的集成初始化
• 直接用大括号给出数组的所有元素的初始值
• 不需要给出数组的大小,编译器替你数数
• 如果给出了数组的大小,但是后⾯面的初始值数量不足,则其后的元素被初始化为0
集成初始化时的定位
C99 ONLY!
• 用[n]在初始化数据中给出定位
• 没有定位的数据接在前面的位置后⾯面
• 其他位置的值补零
• 也可以不给出数组大小,让编译器算
• 特别适合初始数据稀疏的数组
数组的大小
• sizeof给出整个数组所占据的内容的大小,单位是字节
可扩展性!
• sizeof(a[0])给出数组中单个元素的大小,于是相除就得到了数组的单元个数
• 这样的代码,一旦修改数组中初始的数据,不需要修改遍历的代码
数组的赋值
• 数组变量本身不能被赋值
• 要把一个数组的所有元素交给另⼀个数组,必须采用遍历
遍历数组
• 通常都是使用for循环,让循环变量i从0到<数组的长度,这样循环体内最大的i正好是数组最大的有效下标
• 常见错误是:
• 循环结束条件是<=数组长度,或;
• 离开循环后,继续用i的值来做数组元素的下标!
7.2-2数组的例子:素数
判断素数
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测试是否可以整除
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;
}
• 对于n要循环n-1遍
• 当n很大时就是n遍
去掉偶数后,从3到x-1,每次加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是偶数,立刻
• 否则要循环(n-3)/2+1遍
• 当n很大时就是n/2遍
无须到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;
}
• 只需要循环sqrt(x)遍
sqrt
判断是否能被已知的且<x的素数整除
int main(void)
{
const int number = 100;
int prime[number] = {2};
int count = 1;
int i = 3;
while ( count < number ) {
if ( isPrimer(i, prime, count) ) {
prime[count++] = i;
}
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 number0knownPrimes)
{
int ret = 1;
int i;
for ( i=0; i<number0knownPrimes; i++) {
if ( x % knownPrimes[i] ==0) {
ret = 0;
break;
}
}
return ret;
}
构造素数表
• 欲构造n以内的素数表
1. 令x为2
2. 将2x、3x、4x直至ax<n的数标记为非素数
3. 令x为下⼀一个没有被标记为非素数的数,重复2;直到所有的数都已经尝试完毕
• 欲构造n以内(不含)的素数表
1. 开辟prime[n],初始化其所有元素为1,prime[x]为1表示x是素数
2. 令x=2
3. 如果x是素数,则对于(i=2;x*i<n;i++)令prime[i*x]=0
4. 令x++,如果x<n,重复3,否则结束
const int maxNumber = 25;
int isPrime[maxNumber];
int i;
int x;
for ( i=0; i<maxNumber; i++ ) {
isPrime[i] = 1;
}
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");
• 算法不一定和人的思考方式相同
7.2-3 二维数组
二维数组
• int a[3][5];
• 通常理解为a是一个3行5列的矩阵
二维数组的遍历
for ( i=0; i<3; i++ ) {
for ( j=0; j<5; j++ ) {
a[i][j] = i*j;
}
}
• a[i][j]是一个int
• 表示第i行第j列上的单元
• a[i,j]是什么?
二维数组的初始化
int a[][5] = {
{0,1,2,3,4},
{2,3,4,5,6},
};
• 列数是必须给出的,行数可以由编译器来数
• 每行⼀一个{},逗号分隔
• 最后的逗号可以存在,有古老的传统
• 如果省略,表示补零
• 也可以用定位(*C99 ONLY)
tic-tac-toe游戏
• 读入一个3X3的矩阵,矩阵中的数字为1表示该位置上有⼀一个X,为0表示为O
• 程序判断这个矩阵中是否有获胜的一方,输出表示获胜一方的字符X或O,或输出无⼈获胜
读入矩阵
const int size = 3;
int board[size][size];
int i,j;
int numofx;
int numofo;
int result = -1; //-1:没人赢 , 1:x赢, 0:o赢
//读入矩阵
for ( i=0; i<size; i++ ) {
for (j=0; j<size; j++ ) {
scanf("%d", &board[i][j]);
}
}
检查行
//检查⾏
for ( i=0; i<size && result == -1; i++ ) {
numofo = num0fx = 0;
for ( j=0; j<size; j++ ) {
if ( board[i][j] == 1 ) {
numofx ++;
} else {
numofo ++;
}
}
if ( numofo == size ) {
result = 0;
} else if (numofx == size ) {
result = 1;
}
}
检查列
if ( result == -1 ) {
for ( j=0; j<size && result == -1; j++ ) {
numofo = numofx = 0;
for ( i=0; i<size; i++ ) {
if ( board[i][j] == 1 ) {
numofx ++;
} else {
numofo ++;
}
}
if ( numofo == size ) {
result = 0;
} else if (numofx == size ) {
result = 1;
}
}
}
行和列?
能否用一个两重循环来检查行和列?
检查对角线
思考题:
如何用一个两重循环实现行和列的检查
用一个两重循环实现行和列的检查该怎么做?
可以直接一次循环就检查完行和列
这里就简写
i循环体
j循环体
board[i][j] //检查行
语句
board[j][i] //变换一下i,j坐标就可以检查列了