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行
- 数学
在数学中该问题的公式为: (首项+尾项) * 项数 / 2int 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;//取值: 目标位置~lenfor(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否则返回0nums:要查找的数组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;
}/*定义一个函数查找指定数据是否存在如果存在返回下标否则返回-1nums:要查找的数组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;
}