1. 数组
1.1 引入
需求:记录班级10个学员的成绩
需要定义10个变量 存在的问题: 变量名起名困难 变量管理困难
需求:记录班级1000个学员的成绩
1.2 概念
作用:容纳 数据类型相同
的多个数据的容器
。
特点:
- 长度不可变
- 容纳 数据类型相同
名词:
元素:
数组中存储的数据
下标:
元素在数组中的位置,下标从0开始,长度-1结束
长度:
数组中可存储元素个数
注意:
- 数组中的数据在 内存中是连续存储的。
1.3 分类
-
安存储的数据类型存储
字符数组 整形数组 浮点型数组 ...
-
按维度分类
一维数组 二维数组 ...
1.4 使用
1.4.1 定义与初始化
① 定义:
语法:
数据类型 数组名[长度] = {值1, 值2, 值3, 值4...};
② 初始化
全部初始化:
此时可省略长度不写
如:
int nums[5] = {1, 3, 5, 7, 9}
//此时可以长度不写
int nums01[] = {1, 3, 5, 7, 9}
部分初始化:
没初始化的部分,值默认为0。
如:
int num02[5] = {1, 3, 5}
③ 字符数组与字符串
比如:
char str01[5] = {'h','e','l','l','o'};
char str02[] = "hello";
注意:
- 将
字符串
赋值给字符数组 系统将默认
为其在尾部加 \0。- \0:字符串结束
#include <stdio.h> int main(int argc, char const *argv[]) { char str01[5] = {'h','e','l','l','o'}; char str02[] = "hello"; printf("str01大小为:%d\n",sizeof(str01)); printf("str02大小为:%d\n",sizeof(str02)); return 0; } // 输出 // str01大小为:5 // str02大小为:6
1.4.2 使用
① 获取指定位置元素
语法:
数组名[下标]
如:
int nums[5] = {1, 3, 5, 7, 9}; 取出并打印 第4个数 printf("%d\n", nums[3]); 取出后赋值给别的变量 int x = nums[4]; 取出后赋值给数组的其他位置 nums[1] = nums[4];
#include <stdio.h> void fun01() { int nums[5] = {1,3,5,7,9}; printf("nums中下标为4的元素是:%d\n",nums[4]); } void fun02() { int nums[5] = {1,3,5,7,9}; int x = nums[4]; printf("x=%d\n",x); } int main(int argc, char const *argv[]) { fun01(); fun02(); return 0; } // 输出: // nums中下标为4的元素是:9 // x=9
② 修改指定位置元素
语法:
数组名[下标] = 值;
如:
int nums[5] = {1,3,5,7,9}; 将数组中下标为1的元素修改为33 nums[1] = 33;
例:
#include <stdio.h> void fun03() { int nums[5] = {1,3,5,7,9}; nums[1] = 33; printf("nums修改后下标为1的值是:%d\n",nums[1]); } int main(int argc, char const *argv[]) { fun03(); return 0; } // 输出: // nums修改后下标为1的值是:33
③ 计算数组长度
思路:
数组所占字节数大小/单个元素所占大小 = 长度
如:
int nums[] = {1,2,3,4,5,6,7,8,89,9,10,1,21,121,121,21,3,32}; int len = sizeof(nums) / sizeof(nums[0]); int len = sizeof(nums) / sizeof(int);
例:
#include <stdio.h> void fun04() { int nums[] = {1,2,3,4,5,6,7,8,89,9,10,1,21,121,121,21,3,32}; int len = sizeof(nums) / sizeof(nums[0]); //int len = sizeof(nums) / sizeof(int); printf("数组的长度为:%d\n",len); } int main(int argc, char const *argv[]) { fun04(); return 0; } // 输出: // 数组的长度为:18
④ 遍历
含义:将数组中的数据逐个取出
例:
#include <stdio.h> void fun05() { int nums[] = {1,2,3,4,5,6,7,8,89,9,10,1,21,121,121,21,3,32}; //计算数组长度 int len = sizeof(nums) / sizeof(nums[0]); //开启循环 for(int i = 0; i < len; i++) { //取值 int x = nums[i]; //打印 printf("%d,",x); } printf("\n"); } int main(int argc, char const *argv[]) { fun05(); return 0; } // 输出: // 1,2,3,4,5,6,7,8,89,9,10,1,21,121,121,21,3,32,
⑤ 键盘输入数据存储到数组中
注意:字符串除外
例1:
void fun06() { char cs[10] = {0}; scanf("%c",&cs[0]); printf("%c\n",cs[0]); } /* 输入: 56 输出: 5 因为%c是char的占位符, 占一个字节 */
例:
#include <stdio.h> void fun07() { int nums[5] = {0}; for(int i = 0; i < 5; i++) { printf("请输入第%d个数",(i+1)); scanf("%d",&nums[i]); } for(int i = 0; i < 5; i++) { printf("%d,",nums[i]); } printf("\n"); } int main(int argc, char const *argv[]) { fun07(); return 0; } /* 输出: 请输入第1个数12 请输入第2个数23 请输入第3个数12 请输入第4个数23 请输入第5个数25 12,23,12,23,25, */
⑥ 字符串录入
scanf
问题:不能录入空格
void fun08() { char str[50] = {0}; //会将空格或回车作为录入结束 scanf("%s",str); printf("str=%s\n",str); }
gets
问题:会出现bug 假如没有定义数组长度,会报错
#include <stdio.h> void fun09() { char str[] = ""; gets(str,sizeof(str),stdin); printf("str=%s\n",str); } int main(int argc, char const *argv[]) { fun09(); return 0; } /* hello str=hello *** stack smashing detected ***: ./a.out terminated 已放弃 (核心已转储) */
fgets
最优选择 语法: char *fgets(char *restrict str, int size, FILE *restrict stream)) fgets(数组名,sizeof(数组名),stdin); 假如没有定义数组长度,会直接结束。
#include <stdio.h> void fun10() { char str[50] = ""; fgets(str,sizeof(str),stdin); printf("str=%s\n",str); } int main(int argc, char const *argv[]) { fun10(); return 0; } /* 输出: hello world str=hello world */
⑦ 数组与函数
注意:
- 数组的本质是一个地址,
- 所以调用函数时
传入数组
,其实传递的是数组名对应的地址,- 此时当
函数中
将该数组中的内容修改,- 那么调用该函数所
传递的数组的值也将被修改
如:
#include <stdio.h> void funA(int a) { a = 10; } void funB(int nums[5]) { nums[0] = 10; } /* 要求定义一个函数,计算两数之和,要求返回和,加数,被加数 */ void add(int a,int b,int nums[3]) { int x = a + b; nums[0] = x; nums[1] = a; nums[2] = b; } int main(int argc, char const *argv[]) { int x = 1; funA(x); printf("x= %d\n",x); int xs[5] = {1}; funB(xs); printf("xs[0] = %d\n",xs[0]); int adds[3] = {0}; add(10,2,adds); for(int i = 0; i < 3; i++) { printf("%d\n",adds[i]); } return 0; } /* 输出: x= 1 xs[0] = 10 12 10 2 */
1.5 扩展
因为数组本质是地址,所以 遍历时:nums[i] == *(nums+i)
#include <stdio.h>
int main(int argc, char const *argv[])
{
int nums[5] = {2,4,6,8,9};
for(int i = 0; i < 5; i++)
{
printf("%d,",nums[i]);
// printf("%d,",*(nums+i));
}
printf("\n");
return 0;
}
/*
两个打印输出相同,如下:
2,4,6,8,9,
*/
2. 二维数组
2.1 概念
将一维数组
作为元素存储的数组 。
2.2 使用
2.2.1 定义与初始化
语法:
数据类型 数组名[x][y] = {
{值1,值2,值3,...},
{值11,值22,值33,...},
{值111,值222,值333,...},
...
};
x:二维数组中存储的
一维数组的个数
y:
一维数组
中可存储的元素的个数
如:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int nums[5][3] = {
{1,2,3},
{4,5,6},
{7,8,9},
{10,11,12},
{13,14,15}
};
// 当数组全部初始化时可以省略长度不写
// 二维数组的长度是一维数组的个数
int nums[][3] = {
{1,2,3},
{4,5,6},
{7,8,9},
{10,11,12},
{13,14,15}
};
int nums[3][3] = {
{0},
{0},
{0}
};
int nums[3][3] = {0};
int nums[3][3] = {
{1,2,3},
{4,5,6},
{7,8,9}
};
int nums01[3][3] = {1,2,3,4,5,6,7,8,9};
int nums02[3][3] = {0};
return 0;
}
2.2.2 使用
① 取值
获取二维数组中下标为x的一维数组
数组名[x]
获取二维数组中下标为x中下标为y的元素
数组名[x][y]
例:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int nums01[3][3] = {1,2,3,4,5,6,7,8,9};
int nums02[3][3] = {0};
printf("nums01[0][2] = %d\n",nums01[0][2]);
printf("nums02[0][2] = %d\n",nums02[0][2]);
char strs[3][50] = {"hello", "world", "boboy"};
printf("strs[0] = %s\n",strs[1]);
return 0;
}
/*
输出:
nums01[0][2] = 3
nums02[0][2] = 0
strs[0] = world
*/
② 赋值
数组名[x][y] = 值;
注意:
因为数组不能修改其指向的地址(因为数组是常指针),所以数组不能重复初始化
所以以下语法是
错误的
:数组名[x] = 新数组;
常指针:可以修改其指向的内容中的值,但是不能修改其指向的地址
③ 长度
sizeof(数组名) / sizeof(数组中第一个元素) = 长度
④ 遍历
思路:
1,获取二维数组中一维数组的个数,即二维数组的长度,为x
2,获取一维数组中元素的个数,即一维数组的长度,为y
3,开启循环,逐个获取二维数组中的一维数组
4,在循环内遍历获取到的一维数组
例1:
#include <stdio.h>
void fun01()
{
int nums[5][3] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
int xlen = sizeof(nums) / sizeof(nums[0]);
int ylen = sizeof(nums[0]) / sizeof(nums[0][0]);
for(int i = 0; i < xlen; i++)
{
//nums[i]
for(int j = 0; j < ylen; j++)
{
int x = nums[i][j];
printf("%d,",x);
}
printf("\n");
}
}
void fun02()
{
char strs[3][5] = {
{'a','b','c','d','e'},
{'h','e','l','l','o'}
};
int xlen = sizeof(strs) / sizeof(strs[0]);
int ylen = sizeof(strs[0]) / sizeof(strs[0][0]);
for(int i = 0; i < xlen; i++)
{
for(int j = 0; j < ylen; j++)
{
printf("%c ",strs[i][j]);
}
printf("\n");
}
}
int main(int argc, char const *argv[])
{
fun01();
printf("###################\n");
fun02();
fun03();
return 0;
}
/*
输出:
1,2,3,
4,5,6,
7,8,9,
10,11,12,
13,14,15,
###################
a b c d e
h e l l o
*/
例2:遍历字符串
#include <stdio.h>
void fun03()
{
char strs[3][6] = {
"abcde",
"hello"
};
int xlen = sizeof(strs) / sizeof(strs[0]);
for(int i = 0; i < xlen; i++)
{
printf("%s",strs[i]);
printf("\n");
}
}
int main(int argc, char const *argv[])
{
fun03();
return 0;
}
/*
输出:
abcde
hello
*/
例3:键盘录入
#include <stdio.h>
//键盘录入多个字符串存储到数组中
void fun04()
{
char strs[3][128] = {0};
for(int i = 0; i < 3; i++)
{
fgets(strs[i],sizeof(strs[i]),stdin);
}
for(int i = 0; i < 3; i++)
{
printf("%s",strs[i]);
}
}
int main(int argc, char const *argv[])
{
fun04();
return 0;
}
/*
输入:
hello
world
tom
输出:
hello
world
tom
*/
3. 算法
软件 = 算法 + 数据结构
算法 = 数学公式使用代码实现
优点:提高代码运行效率
如:
- 代码
//计算100以内数之和
int sum = 0;
for(int i = 0; i < 100; i++)
{
sum = sum + i;
}
printf("sum=%d\n",sum);
分析得知:
以上代码共执行了303行
- 数学
在数学中该问题的公式为: (首项+尾项) * 项数 / 2
int sum = (0 + 99) * 100 / 2;
分析得知:
以上代码共执行了1行
评判算法是否优良
- 时间复杂度
- 空间复杂度
3.1 常用算法
3.1.1 两数交换位置
int a = 1;
int b = 2;
int c = a;
a = b;
b = c;
3.1.2 寻找最值
思路: 1,假设数组中的一个数为最值 2,在使用数组中元素与其一一对比 3,如果取出的元素大于或小于最值,将假设的最值换为改值 4,当对比完成后,假设的最值就是真的最值
代码:
int nums[] = {6,21,33,67,1,29,32,14}; //寻找最小值 int min = nums[0]; for(int i = 0; i < sizeof(nums)/sizeof(nums[0]); i++) { if(nums[i] < min) { min = nums[i]; } } printf("最小值为:%d\n",min);
寻找最值下标
思路: 1,假设数组中的位置上的数为最小值,记录该位置为最值下标 2,在使用数组中元素与下标对应的值一一对比 3,如果取出的元素大于或小于下标对应的值,将假设的下标改为该数的下标 4,当对比完成后,假设的最下标就是最值下标
代码:
int nums[] = {6,21,33,67,1,29,32,14};排序 //寻找最小值下标 int minIndex = 0; for(int i = 0; i < sizeof(nums)/sizeof(nums[0]); i++) { if(nums[i] < nums[minIndex]) { minIndex = i; } } printf("最小值下标为:%d\n",minIndex);
3.2 排序
目的:将数组中的数据从大到小(降序)或从小到大(升序)排序
3.2.1 冒泡排序
核心思想:相邻比较,交换位置
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
//生成随机数
void setNums(int nums[],int len)
{
for(int i = 0; i < len; i++)
{
nums[i] = rand() % 100;
}
}
//打印数组中的内容
void printNums(int nums[],int len)
{
for(int i = 0; i < len; i++)
{
printf("%d ",nums[i]);
}
printf("\n");
}
void sort01(int nums[],int len)
{
//冒泡排序
for (int i = 0; i < len - 1; i++)
{
for(int j = 0; j < len-1; j++)
{
if (nums[j] > nums[j+1])
{
//交换位置
int x = nums[j];
nums[j] = nums[j+1];
nums[j+1] = x;
}
}
}
}
int main(int argc, char const *argv[])
{
int nums[10] = {0};
srand(time(NULL));
int len = sizeof(nums)/ sizeof(nums[0]);
setNums(nums,len);
printNums(nums,len);
sort01(nums,len);
printNums(nums,len);
return 0;
}
//25 70 74 40 78 42 91 54 50 20
//20 25 40 42 50 54 70 74 78 91
3.2.2 选择排序
核心思想:确定位置,与最值,交换位置
void sort02(int nums[],int len)
{
//选择排序,外层循环一次找到一个最值并且放到目标位置
for(int j = 0; j < len -1 ; j++)
{
int tagIndex = j;
int minIndex = tagIndex;
//取值: 目标位置~len
for(int i = tagIndex; i < len; i++)
{
//寻找最值下标
if(nums[minIndex] > nums[i])
{
minIndex = i;
}
}
//目标位置不等于最值位置,则交换位置
if (tagIndex != minIndex)
{
//交换位置,此时最值交换到目标位置
int x = nums[tagIndex];
nums[tagIndex] = nums[minIndex];
nums[minIndex] = x;
}
}
}
//7 82 4 2 42 19 69 70 19 52
//2 4 7 19 19 42 52 69 70 82
简化:
void sort02(int nums[],int len)
{
for(int j = 0; j < len -1 ; j++)
{
int minIndex = j;
for(int i = j; i < len; i++)
{
if(nums[minIndex] > nums[i])
{
minIndex = i;
}
}
if (j != minIndex)
{
//交换位置
int x = nums[j];
nums[j] = nums[minIndex];
nums[minIndex] = x;
}
}
}
//7 82 4 2 42 19 69 70 19 52
//2 4 7 19 19 42 52 69 70 82
3.3 查找
目的:查询数组中是否存在指定元素
3.3.1 顺序查找
核心思想:逐个比较
/*
定义一个函数查找指定数据是否存在
如果存在返回1
否则返回0
nums:要查找的数组
len:数组长度
tag:查找的数据
*/
int find01(int nums[],int len,int tag)
{
for(int i = 0; i < len; i++)
{
if (nums[i] == tag)
{
return 1;
}
}
return 0;
}
/*
定义一个函数查找指定数据是否存在
如果存在返回下标
否则返回-1
nums:要查找的数组
len:数组长度
tag:查找的数据
*/
int find02(int nums[],int len,int tag)
{
for(int i = 0; i < len; i++)
{
if (nums[i] == tag)
{
return i;
}
}
return -1;
}
3.3.2 二分查找
要求:查找的数组必须 是有序的
int find03(int nums[],int len,int tag)
{
//排序
sort01(nums,len);
printNums(nums,len);
//二分查找法
//定义开始和结束下标
int start = 0;
int end = len - 1;
while(start <= end)
{
//中间值下标
int center = (start + end) / 2;
//等于直接返回
if (nums[center] == tag)
{
return center;
}
else if(nums[center] < tag)
{
start = center + 1;
}
else
{
end = center - 1;
}
}
return -1;
}