对于上一节的项目实战,大家可能都有点兴趣。那么这节我们就来一起学习C语言数组的相关知识吧。
在开始之前先考虑下面两个问题:
1.输入100个数并以与输入时相反的顺序输出这100个数。
2.输入100个学生的成绩,输出高于平均分的那些成绩
一、概述
定义:数组是一组相同类型元素的集合
特点:所有数据元素类型相同、可以存储一个及以上的同类型元素
要素:数组名,集合类型,下标。
二、一维数组
1.一维数组的定义
*定义方式:
类型说明符 数组名[整型常量表达式];
#define Length 100
char ch[Length];
int arr[10];
double arr2[5*3];
*说明:命名规则、数组长度、下标、数组类型
数组的命名规则和变量的命名类似,在此基础上加上[常量值]即可,[]是一种操作符,叫做下标引用操作符,用来访问数组元素的。数组是一种特殊的变量,它也是有类型的,例如int arr[10]的数据类型就是int [10]。数组的长度可以使用sizeof()运算符获取:
int arr[10]={0};
int length_arr = sizeof(arr)/sizeof(int);//10
2.一维数组的引用
*引用方式:数组名[下标];
如a[0],x[i],y[2*i-1];
例:
main()
{
int a[100];
for(int i=0;i<100;i++)
{
scanf("%d",&a[i]);
}
for(int i=0;i<sizeof(a)/sizeof(int);i++)
{
printf("%4d",a[99-i]);
}
}
将a[i]当作一个int类型的数据,再看这段代码,是不是就不吃力了。这时候转换回来,再看a[i],这就是对数组元素的引用。
3.一维数组的初始化
*在定义数组时,对全部数组元素赋初值。
*只给一部分元素赋初值
*定义数组时使数组中全部元素自动赋0值
int arr[5]={0,1,2,3,4};//全部初始化
int arr[5]={6,2};//部分初始化
int arr[5]={6,2,0,0,0};//与上一行等价
int arr[5]={0};//全部赋0值
static int arr[5];//静态变量定义时不初始化,则自动初始化为0
*对全部元素赋初值时,可以不指定数组长度。
int arr[3]={1,2,3};
int arr[]={1,2,3};//二者完全等价
//后者[]中隐藏数字3
4.相关算法介绍
(1)Fibonacci数列问题
//用数组求斐波那契数列前四十项
int main()
{
long f[40]={1,1};
for(int i=2;i<40;i++)
f[i]=f[i-1]+f[i-2];
for(int i=0;i<40;i++)
{
if(i%5==0)printf("\n");//换行
printf("%12ld",f[i]);
}
return 0;
}
(2)找最值
//举例寻求最大值
int main()
{
int arr[10]={0};
for(int i=0;I<10;i++)scanf("%d",&arr[i]);
int max=arr[0];
for(int i=1;i<10;i++)
{
if(arr[i]>max) max=arr[i];
}
printf("最大值:%d",max);
}
(3)数组合并
int* Add(int arr1[], int arr2[])
{
//将arr2并入arr1
const int len1 = sizeof(arr1) / sizeof(int);
const int len2 = sizeof(arr2) / sizeof(int);
int arr[len1+len2] = { 0 };
for (int i = 0; i < len1; i++)
{
arr[i] = arr1[i];
}
for (int i = len1; i < len1 + len2; i++)
{
arr[i] = arr2[i - len1];
}
return arr;
}
(4)排序问题
冒泡排序
void BubbleSort(int *arr, int size)
{
int i, j, tmp;
for (i = 0; i < size - 1; i++)
{
for (j = 0; j < size - i - 1; j++)
{
if (arr[j] > arr[j+1])
{
tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
选择排序
void SelectionSort(int *arr, int size)
{
int i, j, k, tmp;
for (i = 0; i < size - 1; i++)
{
k = i;
for (j = i + 1; j < size; j++)
{
if (arr[j] < arr[k])
{
k = j;
}
}
tmp = arr[k];
arr[k] = arr[i];
arr[i] = tmp;
}
}
插入排序
void InsertionSort(int *arr, int size)
{
int i, j, tmp;
for (i = 1; i < size; i++)
{
if (arr[i] < arr[i-1])
{
tmp = arr[i];
for (j = i - 1; j >= 0 && arr[j] > tmp; j--)
{
arr[j+1] = arr[j];
}
arr[j+1] = tmp;
}
}
}
希尔排序
void ShellSort(int *arr, int size)
{
int i, j, tmp, increment;
for (increment = size/ 2; increment > 0; increment /= 2)
{
for (i = increment; i < size; i++)
{
tmp = arr[i];
for (j = i - increment; j >= 0 && tmp < arr[j]; j -= increment)
{
arr[j + increment] = arr[j];
}
arr[j + increment] = tmp;
}
}
}
归并排序(数组、递归、函数)
#define MAXSIZE 100
void Merge(int *SR, int *TR, int i, int middle, int rightend)
{
int j, k, l;
for (k = i, j = middle + 1; i <= middle && j <= rightend; k++) {
if (SR[i] < SR[j]) {
TR[k] = SR[i++];
} else {
TR[k] = SR[j++];
}
}
if (i <= middle) {
for (l = 0; l <= middle - i; l++) {
TR[k + l] = SR[i + l];
}
}
if (j <= rightend) {
for (l = 0; l <= rightend - j; l++) {
TR[k + l] = SR[j + l];
}
}
}
void MergeSort(int *SR, int *TR1, int s, int t)
{
int middle;
int TR2[MAXSIZE + 1];
if (s == t) {
TR1[s] = SR[s];
} else {
middle = (s + t) / 2;
MergeSort(SR, TR2, s, middle);
MergeSort(SR, TR2, middle + 1, t);
Merge(TR2, TR1, s, middle, t);
}
}
快速排序(数组、递归、函数)
void QuickSort(int *arr, int maxlen, int begin, int end)
{
int i, j;
if (begin < end) {
i = begin + 1;
j = end;
while (i < j) {
if(arr[i] > arr[begin]) {
swap(&arr[i], &arr[j]);
j--;
} else {
i++;
}
}
if (arr[i] >= arr[begin]) {
i--;
}
swap(&arr[begin], &arr[i]);
QuickSort(arr, maxlen, begin, i);
QuickSort(arr, maxlen, j, end);
}
}
void swap(int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
桶排序(进阶)
void bucketSort(int *arr, int size, int max)
{
int i,j;
int buckets[max];
memset(buckets, 0, max * sizeof(int));
for (i = 0; i < size; i++) {
buckets[arr[i]]++;
}
for (i = 0, j = 0; i < max; i++) {
while((buckets[i]--) >0)
arr[j++] = i;
}
}
堆排序(进阶)
void Heapify(int *arr, int m, int size)
{
int i, tmp;
tmp = arr[m];
for (i = 2 * m; i <= size; i *= 2) {
if (i + 1 <= size && arr[i] < arr[i+1]) {
i++;
}
if (arr[i] < tmp) {
break;
}
arr[m] = arr[i];
m = i;
}
arr[m] = tmp;
}
void BulidHeap(int *arr, int size)
{
int i;
for (i = n / 2; i > 0; i--) {
Heapify(arr, i, size);
}
}
void swap(int *arr, int i, int j)
{
int tmp;
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
void HeapSort(int *arr, int size)
{
int i;
BulidHeap(arr, size);
for (i = size; i > 1; i--) {
swap(arr, 1, i);
Heapify(arr, 1, i - 1);
}
}
计数排序(进阶)
void CountingSort(int *A, int *B, int n, int k)
{
int *C = (int *)malloc(sizeof(int) * (k + 1));
int i;
for (i = 0; i <= k; i++) {
C[i] = 0;
}
for (i = 0; i < n; i++) {
C[A[i]]++;
}
for (i = 1; i <= k; i++) {
C[i] = C[i] + C[i - 1];
}
for (i = n - 1; i >= 0; i--) {
B[C[A[i]] - 1] = A[i];
C[A[i]]--;
}
}
基数排序(进阶)
int get_index(int num, int dec, int order)
{
int i, j, n;
int index;
int div;
for (i = dec; i > order; i--) {
n = 1;
for (j = 0; j < dec - 1; j++)
n *= 10;
div = num / n;
num -= div * n;
dec--;
}
n = 1;
for (i = 0; i < order - 1; i++)
n *= 10;
index = num / n;
return index;
}
void RadixSort(int *arr, int len, int dec, int order)
{
int i, j;
int index;
int tmp[len];
int num[10];
memset(num, 0, 10 * sizeof(int));
memset(tmp, 0, len * sizeof(int));
if (dec < order) {
return;
}
for (i = 0; i < len; i++) {
index = get_index(arr[i], dec, order);
num[index]++;
}
for (i = 1; i < 10; i++) {
num[i] += num[i-1];
}
for (i = len - 1; i >= 0; i--) {
index = get_index(arr[i], dec, order);
j = --num[index];
tmp[j] = arr[i];
}
for (i = 0; i < len; i++) {
arr[i] = tmp[i];
}
RadixSort(arr, len, dec, order+1);
}
(5)逆序重放
void Reserve(int arr[])
{
int len = sizeof(arr) / sizeof(int) - 1;
for (int i = 0; i <= len / 2; i++)
{
int tmp = arr[i];
arr[i] = arr[len - i];
arr[len - i] = tmp;
}
}
三、二维数组
1.二维数组的定义
前面学习的数组被称为一维数组,数组的元素都是内置类型的,如果我们把一维数组作为数组的元素,这时候就是二维数组。二维数组作为数组元素创建的数组称为三维数组,但二维以上统称为多维数组。
那么二维数组如何定义和创建呢?类比一维数组就有:(二维数组的引用同一位数组一样)
int arr[3][5];
char ch[5][3];
2.二维数组的初始化
*对全部元素赋值
int arr[3][3]={{0,1,2},{1,2,3},{2,3,4}};
int arr[3][3]={0,1,2,1,2,3,2,3,4};
int arr[][3]={0,1,2,1,2,3,2,3,4};
//初始化可以省略行,不能省略列
*对部分元素赋值
int arr[3][3]={{1,2,3},{2,3,4}};
int arr[3][3]={{1,2,0},{2,4,0}}
int arr[3][3]={{1,2},{2,4}};//等价于上行
int arr[3][3]={{1,2,2},{4}};
int arr[3][3]={1,2,2,4};//等价于商上一行
二维数组赋值也就那么几种形式,大家可以回去琢磨一下,亲自上手实践。
四、字符数组
字符数组是数组元素都是char类型的一种特殊数组,学习本节内容有很大的实际运用意义,例如打印图形,了解ASCII码对应的字符等。
1.字符数组的定义
定义方法无非是数据类型是char类型。
char ch[10];
char c[2][5];
2.字符数组的初始化
*将字符数组中的各元素逐个赋予字符
*用字符串常量来对字符数组初始化
char ch[5]={'t','a','b','l','e'};
char str[]="geography";
char str[10]="geography";//二者等价
const char str[10]="geography";
const char *str="geography";//二者等价
注:对静态字符数组未被赋值的元素系统自动将其赋值为空‘\0’;
注:使用字符串常量赋值时,要考虑字符串本身结尾有‘\0’存在,所以上述代码存了10个字符。
字符数组的引用:同简单数组。
3.字符数组的输入输出
%c逐个字符输入/输出;%s一次性输入/输出整个字符串。
举例:
void fuc01()
{
char ch[6];
scanf("%s",ch);
printf("%s\n", ch);
}
void fuc02()
{
char ch[6];
for (int i = 0;i < 6;i++)
{
scanf("%c", &ch[i]);
}
for (int i = 0;i < 6;i++)
{
printf("%c", ch[6]);
}
}
输入输出时应注意:第一、“%s”输入,后面跟数组名,不带&符;第二、用scanf输入(%s格式)时,无法输入空格;第三、%s格式输出,遇到‘\0’时结束。printf函数输出项是字符数组名,不是数组元素。
4.字符的相关io函数
(1)字符输出函数---putchar();
(2)字符输入函数---getchar();
(3)字符串处理函数
gets(字符数组名)---字符串输入函数;
puts(字符数组名或字符串常量)---字符串输出函数;
5.<string.h>头文件介绍
strlen(str):求字符串有效长度
strcat(str1,str2):字符串连接
strcpy(str1,str2):字符串复制
strcmp(str1,str2):字符串比较//比较规则:字典序
五、指针数组
指针数组是数组每一个元素存储的都是另一个同类型数据的地址
六、自定义类型数组
自定义类型数组是数组存储的元素是自定义的数据类型,像struct、enum类型
*七、C99中的边长数组
在C99标准之前,C语言在创建数组的时候,数组的大小指定只能使用常量、常量表达式,或者如果我们初始化数据时省略数组大小。但这样的语法限制,让我们创建数组时不够灵活,有时候数组大了浪费空间、有时候小了不够用。
C99中给了一个变长数组(Variable-Length Array,简称VAL)的新特性,允许我们使用变量指定数组大小。即:
int n;
scanf("%d",&n);
int arr[n];
变长数组的根本特征,就是数组长度只有运行时才能确定,所以变长数组不能初始化。它的好处是程序员不必在开发时,随意为数组指定一个估计的长度,程序可以在运行时为数组分配精确的长度。有一个比较迷惑的点,变长数组的意思是数组的大小是可以使用变量来指定的,在程序运行的时候,根据变量的大小来指定数组的元素个数,而不是说数组的大小是可变的。数组的大小一旦确定就不能再变化了。
注:vs2022上虽然支持大部分C99的语法,但不支持C99的边长数组,但可以使用gcc编译器测试验证(推荐Devc++)。大家可以复制这段代码测试结果。
#include <stdio.h>
int main()
{
int n=0;
scanf("%d",&n);
int arr[n];
for(int i=0;i<n;i++)
{
scanf("%d",&arr[i]);
printf("%d ",arr[i]);
}
return 0;
}
感谢观看。