数组
1. 问题的引入
int a;//定义一个整型
int a1,a2,a3,…a99;
有没有一种办法可以一次性的定义一组相同类型的变量?
数组。
2. 数组
一组具有相同类型的数组元素的有序集合。
在C语言中:
一维数组
二维数组
三维数组
。。。。
但是实际上我们的C语言中只有一维数组。
3. 一维数组
(1) 定义格式
类型说明符 数组名[整型表达式] {={初始值列表}};
“类型说明符”:指定数组中元素的类型,而不是数组的类型!
可以是C语言中任意合法的类型。
“数组名”:数组的名字。“标识符”
“整型表达式”:指定数组中元素的个数的。
C语言规定,在定义数组时,需要指定数组元素的个数,“常量表达式”。
例子:
int a[10];//定义了一个数组,数组名为a,里面包含了10个int类型的元素。
int是数组元素的类型,不是数组a的类型。
typeof(4) <==> int
typeof(a) <==> int [10]
求一个对象的类型小技巧:
获取到这个对象的完整的定义,然后把对象的名字从定义中去除,剩下的就是
这个对象的类型。
一维数组在内存中的存放:
在连续的地址空间中,从低地址到高地址依次连续存放数组中的每一个元素。
(2) 一维数组中元素的引用(访问)
char a[5];
引用数组元素的方法:
数组名[下标]
下标:C语言约定数组元素的下标是从0开始的。
引用数组元素和引用普通变量是一样的。
数组元素也有左值和右值的。
a[0] = 'A';
a[0]代表的是某一块内存空间(左值)
char b = a[0];
a[0]代表是某一块内存空间中的值(右值)
(3) 一维数组的初始化
初始化是指:定义对象时,就指定对象的值。
数组的初始化是用{}
-
全部初始化
int a[10] = {0,1,2,3,4,5,6,7,8,9}; -
可以对部分元素进行初始化,后面的元素自动初始化为0
int a[10] = {1,2,3};
int a[10] = {0}; -
如果对全部数组元素进行初始化,那就可以不指定数组的长度
int a[] = {0,1,2,3,4};注意:
int a[10];
a[10] = {0,1,2,3,4,5,6,7,8,9};//error
a = {0,1,2,3,4,5,6,7,8,9};//errorprintf("%d\n",a);
char c[10];
printf("%c\n",c);练习:
-
定义一个5个整型元素的数组,并通过键盘获取这5个元素的值,然后打印到终端的。
-
定义一个int类型数组,从键盘上获取值,求该数组中各元素之和、元素最大值以及元素
的最小值。 -
判断一个一维数组a[5]是否为递增
a[0] <= a[1] <= a[2] <= a[3] <= a[4]
a[0] <= a[1] && a[1] <= a[2] && … && a[3] <= a[4]if(a[i] > a[i+1]) { printf("不是递增"); }
-
把每一个元素按一定的规则放置到合适的位置上去。
对一个数组进行排序。
冒泡法:
相邻的两个元素,两两进行比较,把较大者往后移(两两交换)int a[10]; //第一轮 if(a[0] > a[1]) { a[0] <===> a[1]//互换 } if(a[1] > a[2]) { a[1] <===> a[2] } .... if(a[n - 2] > a[n - 1]) { a[n-2] <===> a[n-1] } //第一轮 int i; int temp; for(i = 0;i < 9;i++) { if(a[i] > a[i+1]) { temp = a[i]; a[i] = a[i+1]; a[i+1] = temp; } } //第二轮 if(a[0] > a[1]) { a[0] <===> a[1]//互换 } if(a[1] > a[2]) { a[1] <===> a[2] } .... if(a[n - 3] > a[n - 2]) { a[n-2] <===> a[n-1] } //第二轮 int i; int temp; for(i = 0;i < 8;i++) { if(a[i] > a[i+1]) { temp = a[i]; a[i] = a[i+1]; a[i+1] = temp; } } //第三轮 ..... //总结: int i,n; int temp; for(n = 9;n >= 1;n--) { for(i = 0;i < n;i++) { if(a[i] > a[i+1]) { temp = a[i]; a[i] = a[i+1]; a[i+1] = temp; } } }
选择法:
1. 选择一个最大的元素
2. 把最大的元素与最后一个元素进行位置互换//第一轮 int a[5],temp; int i,i_max = 0; int max = a[0]; for(i = 0;i < 5;i++) { if(a[i] > max) { max = a[i];//记录数组中的最大值 i_max = i;//记录数组中最大值元素的下标 } } if(i_max != 4) { temp = a[4]; a[4] = a[i_max]; a[i_max] = temp; } //第二轮 int i,i_max = 0; int max = a[0]; for(i = 0;i < 4;i++) { if(a[i] > max) { max = a[i];//记录数组中的最大值 i_max = i;//记录数组中最大值元素的下标 } } if(i_max != 3) { temp = a[3]; a[3] = a[i_max]; a[i_max] = temp; } //第三轮 ... //总结: int n; for(n = 5;n >= 2;n--) { int i,i_max = 0; int max = a[0]; for(i = 0;i < n;i++) { if(a[i] > max) { max = a[i];//记录数组中的最大值 i_max = i;//记录数组中最大值元素的下标 } } if(i_max != n - 1) { temp = a[n - 1]; a[n - 1] = a[i_max]; a[i_max] = temp; } } ....
-
求斐波拉契数列的前二十项之和。
1 1 2 3 5 8 13… -
不用排序,把一个数组中的负数放在数组的前面。
8 -1 5 -6 7 9 -3
-
求一个一维数组中的第二大的元素,不用排序。
//跟着老大走,你就是老二
-
(选做)
假设有一个数组a[n],能不能从数组a中任选m个元素(m <= n),使其和为k
n = 10,m = 5,k = 10
int a[10] = {-1,6,7,-5,3,2,-4,1,10,-8};穷举:把所有的子数组全部都找出来进行判断:
1. 子序列的个数要为m个。
2. 子序列中的元素的和必须为k。int a[2] = {1,2};
{}
{1}
{2}
{1,2}
共有4种子数组int a[3] = {1,2,3};
{}
{1} {2} {3}
{1,2} {1,3} {2,3}
{1,2,3}
共有8种子数组…
推论:
int a[n]一共有2^n种子数组。int a[5] = {1,2,3,4,5};
22222int a[2] = {1,2};
a[1] a[0]
i = 0 0 0 {}
i = 1 0 1 {1}
i = 2 1 0 {2}
i = 3 1 1 {1,2}int a[3] = {1,2,3};
a[2] a[1] a[0]
i = 0 0 0 0 {}
i = 1 0 0 1 {1}
i = 2 0 1 0 {2}
i = 3 0 1 1 {1,2}
i = 4 1 0 0 {3}
i = 5 1 0 1 {1,3}
i = 6 1 1 0 {2,3}
i = 7 1 1 1 {1,2,3}//i的第xbit为1代表选中了a[x],为0代表没有选中 //遍历所有的子数组 for(i = 0;i < (1 << sizeof(a)/sizeof(int));i++) { //i表示的第几种子数组 int sum = 0;//子数组中各元素之和 int num = 0;//子数组中各元素的个数 //遍历i的低sizeof(a)/sizeof(int)个bit for(x = 0;x < sizeof(a)/sizeof(int);x++) { if(i & (1 << x))//判断i的第xbit是否为1 { //i = 5 a[0] + a[2] //101 i //001 1 << x(x == 0) //001 代表选中了a[0] //101 i //010 1 << x(x == 1) //000 代表没有选中a[1] //101 i //100 1 << x(x == 2) //100 代表选中了a[2] sum += a[x]; num++; } } if(num == m && sum == k) { //第i种子数组满足要求 //将第i中子数组打印出来即可 } }
-
-
-
查找一个元素
对于数组常用的操作:
增、删、改、查增:假设有一个有序数组a,这个数组足够大,现在需要将一个从键盘上输入的整型
插入到这个数组中去,并且还要保证这个数组的有序性。删:略
改:略查:在一个递增数组a[n]中查找一个值为x的元素,如果找到了则返回这个元素的下标
如果没有找到则返回-1.(1) 遍历查找法 int i; for(i = 0;i < n;i++) { if(a[i] == x) { return i; } } return -1; low逼法。 (2) 折半查找法
练习:
求出一个数组中连续的子数组之和中的最大值。 穷举法: 把所有的连续的子数组全部列举出来,然后求累加和,最后比较 以a[0]开头的子数组: a[0] a[0] a[1] a[0] a[1] a[2] ...... a[0] a[1] a[2] ... a[9] int max = a[0]; int sum = 0; for(i = 0;i < sizeof(a)/sizeof(int);i++) { sum += a[i]; if(sum > max) { max = sum; } } 以a[1]开头的子数组: a[1] a[1] a[2] a[1] a[2] a[3] ...... a[1] a[2] a[3] ... a[9] int sum = 0; for(i = 1;i < sizeof(a)/sizeof(int);i++) { sum += a[i]; if(sum > max) { max = sum; } } int max = a[0]; for(j = 0;j < sizeof(a)/sizeof(int);j++) { int sum = 0; for(i = j;i < sizeof(a)/sizeof(int);i++) { sum += a[i]; if(sum > max) { max = sum; } } }
-
二维数组
int a[4];
定义了一个一维数组,数组名为a,数组a中一共有4个int类型的元素,分别是a[0] a[1] a[2]
a[3]。
我们在定义数组a的同时,也声明了一个新的类型,“像a这样的类型”。typeof(a) —> int [4]//a的类型
我们如果想定义3个a这样的类型,该怎么定义?
类型说明符 数组名[整型表达式] {={初始值列表}};
int [4] b[3];//定义了一个数组,数组名为b,里面包含了3个int [4]这种类型的元素。
====>int b[3][4];//二维数组int [4] b[3]; ====>int b[3][4];//二维数组
定义了一个数组b,里面含有三个元素:
b[0] 里面又包含了4个int,b[0]又是一个数组,数组名为b[0]
b[1] 里面又包含了4个int,b[1]又是一个数组,数组名为b[1]
b[2] 里面又包含了4个int,b[2]又是一个数组,数组名为b[2]总结:C语言中,二维数组实际上就是一维数组,只不过该一维数组中的每一个元素又是
一个一维数组。但是,为了便于理解,我们将int b[3][4]理解为一个3行4列的矩阵。
二维数组的定义:
类型说明符 数组名[整型表达式][整型表达式] {={初始值列表}};
代表多少行 代表多少列二维数组在内存中的存放:
按行存放,即先顺序存放第一行的元素,再存放第二行的元素。。。。。二维数组的引用:
数组名[第几行][第几列]引用二维数组的元素和引用普通变量的方式是一模一样的。 也会有左值和右值之分。 a[0][2] = 2; int b = a[0][2];
二维数组的初始化:
(1) 分行给二维数组赋初值int b[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
(2) 将所有数组全部写在一个花括号内,按数组的排序顺序对各元素进行初始化。
int b[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
(3) 对部分元素赋初值,其余的元素自动初始化为0
int b[3][4] = {1,2,3,4}; int b[3][4] = {{1},{2},{3,4}};
(4) 如果对全部元素都赋初值,则定义数组时可以对第一维的长度省略,
但第二维的长度不能省略。int b[][4] = {1,2,3,4,5,6,7,8,9,10}; int b[3][4]; b[3][4] = {1,2,3,4,5,6,7,8,9,10};//error b = {1,2,3,4,5,6,7,8,9,10};//error