【1】冒泡排序
int a[5] = {5,4,3,2,1};
1》概念
相邻的两个数进行比较(两两比较),如果前者大就交换位置
2》排序思想
第一轮:
5 4 进行比较 前者大 交换 ——> 4 5 3 2 1
5 3 进行比较 前者大 交换 ——> 4 3 5 2 1
5 2 进行比较 前者大 交换 ——> 4 3 2 5 1
5 1 进行比较 前者大 交换 ——> 4 3 2 1 5
第二轮:
4 3 进行比较 前者大 交换 ——> 3 4 2 1 5
4 2 进行比较 前者大 交换 ——> 3 2 4 1 5
4 1 进行比较 前者大 交换 ——> 3 2 1 4 5 // 4 在前面和 5 已经交换过了,不用再交换了
第三轮:
3 2 进行比较 前者大 交换 ——> 2 3 1 4 5
3 1 进行比较 前者大 交换 ——> 2 1 3 4 5 // 3 在前面和 4 、5 都已经交换过了,不用再交换了
第四轮:
2 1 进行比较 前者大 交换 ——> 1 2 3 4 5 //2 在前面和 3 、4 、5 都已经交换过了,不用再交换了
3》规律
交换的轮数(外层循环):由数组的元素个数决定,轮数 = 元素个数 -1
每一轮交换的次数(内层循环):也是由数组元素个数决定,次数 = 元素个数 - 1 - 外层循环变量
4》代码展示
#include <stdio.h>
int main(){
int a[5] = {2,4,2,8,3};
int i,j;
int n = sizeof(a) / sizeof(int);
for(i = 0;i < n;i++){
printf("%d ",a[i]);
}
printf("\n");
for(i = 0;i < n - 1;i++)
for(j = 0;j < n - 1 - i;j++){
if(a[j] > a[j+1]){
int t = a[j];
a[j] = a[j+1];
a[j+1] = t;
}
}
for(i = 0;i < n;i++){
printf("%d ",a[i]);
}
printf("\n");
return 0;
}
5》运行结果
【2】选择排序
1》概念
在n个数中找最小值的下标暂时存储,和正确位置交换
2》排序思想
int a[5] = {5,4,3,2,1};
找最小值
第一轮:
将第一个数暂定为最小值,下标 k 设为0 k = 0 a[k] = 5 5(k) 4 3 2 1
a[k] = 5 a[1] = 4 比较 4小于5 把 4 的下标赋给 k k = 1 a[k] = 4 5 4(k) 3 2 1
a[k] = 4 a[2] = 3 比较 3小于4 把 3 的下标赋给 k k = 2 a[k] = 3 5 4 3(k) 2 1
a[k] = 3 a[3] = 2 比较 2小于3 把 2 的下标赋给 k k = 3 a[k] = 2 5 4 3 2(k) 1
a[k] = 2 a[4] = 1 比较 1小于2 把 1 的下标赋给 k k = 4 a[k] = 1 5 4 3 2 1(k)
这时 k 下标所代表的值就是最小的值,把这个值和最前面的值进行交换 1 4 3 2 5
第二轮:
将第二个数暂定为最小值,下标 k 设为 1 k = 1 a[k] = 4 1 4(k) 3 2 5
a[k] = 4 a[2] = 3 比较 3小于4 把3的下标赋给k k = 2 a[k] = 3 1 4 3(k) 2 5
a[k] = 3 a[3] = 2 比较 2小于3 把2的下标赋给k k = 3 a[k] = 2 1 4 3 2(k) 5
a[k] = 2 a[4] = 5 比较 5 大于2 下标不用改变 k = 3 a[k] = 2 1 4 3 2(k) 5
这时 k 下标所代表的数就是该轮最小的值,把这个值和该轮最前面的值进行交换 1 2 3 4 5
第三轮:
将第三个数暂定为最小值,下标 k 设为 2 k = 2 a[k] = 3 1 2 3(k) 4 5
a[k] = 3 a[3] = 4 比较 4大于3 下标不用改变 k = 2 a[k] = 3 1 2 3(k) 4 5
a[k] = 3 a[4] = 5 比较 5大于3 下标不用改变 k = 2 a[k] = 3 1 2 3(k) 4 5
这时 k 下标所代表的数就是该轮最小的值,下标没变,不用进行交换 1 2 3 4 5
第四轮:
将第三个数暂定为最小值,下标 k 设为 3 k = 3 a[k] = 4 1 2 3 4(k) 5
a[k] = 4 a[4] = 5 比较 5大于4 下标不用改变 k = 3 a[k] = 4 1 2 3 4(k) 5
这时 k 下标所代表的数就是该轮最小的值,下标没变,不用进行交换 1 2 3 4 5
3》规律
交换的轮数(外层循环):由数组的元素个数决定,轮数 = 元素个数 - 1
每轮交换的次数(内层循环):该轮要比较的元素个数,次数 = 元素个数 - 1 - 外层循环变量
4》代码展示
#include<stdio.h>
int main(){
int a[5] = {5,6,3,1,2};
int i,j;
int n = sizeof(a) / sizeof(int);
for(i = 0;i < n - 1;i++){
int k = i;
for(j = i + 1; j < n;j++){
if(a[k] > a[j]){
k = j;
}
}
if(k != i){
int t = a[k];
a[k] = a[i];
a[i] = t;
}
}
for(i = 0;i < n;i++){
printf("%d ",a[i]);
}
return 0;
}
5》运行结果
【3】冒泡排序和选择排序的区别
共同点
1)都有两层循环,外层循环控制比较的轮数,和数组元素的个数有关,内层循环控制需要参与比较的元素个数,和外层循环的轮数有关
2)两种算法进行交换的结构是相同的。
3)两种算法虽然效率不一样,但是最终比较的次数是一样的
不同点
冒泡排序第一轮交换完成最大值在最后;选择排序第一轮结束最小值在最前
【4】二维数组
1》格式
存储类型 数据类型 数组名[行数][列数]
例:int arr[2][3]; //两行三列的二维数组
int arr[2][3] = {1,2,3,4,5,6};
2》访问
数组名[行下标][列下标]
第 1 行第 1 列 第一个元素 :arr[0][0] 行下标和列下标都是从零开始
第 2 行第 3 列元素:arr[1][2] arr[行数 - 1][列数 - 1]
行和列都不能越界
int arr[][3] = {1,2,3,4,5,6,7}; //正确
int arr[2][] = {1,2,3,4}; //错误
二维数组定义时行数可以省略,但是列数不能省略
3》数组的元素个数
元素个数 = 行数 * 列数
4》数组大小
1)数组大小 = 行数 * 列数 *数据类型的大小
2)sizeof(数组名)
5》数组名
数组名 arr:第一行的首地址
arr + 1:第二行的首地址
arr [0]:第一行第一列的首地址
6》初始化
1)全部初始化
int arr[2][3] = {1,2,3,4,5,6}; //顺序赋值
int arr[2][3] = {{1,2,3},{4,5,6}}; //按行赋值
2)部分初始化: 未赋值的部分自动初始化为0
int arr[2][3] = {1,2,3,4}; //顺序赋值 123400
int arr[2][3] = {{1,2},{4}}; //按行赋值 120400
3)未初始化:所有的值都是随机值,后期赋值单独进行
int arr[2][3];
arr[0][0] = 1;arr[0][1] = 1; //单独赋值
7》二维数组的遍历
双重for循环 : 外层循环控制行,内层循环控制列
练习:
在终端输入一个3行3列的数,并输出
#include <stdio.h>
int main(){
int arr[3][3] = {};
for(int i = 0;i < 3;i++){
for(int j = 0;j < 3;j++){
scanf("%d",&arr[i][j]);
}
}
for(int i = 0;i < 3;i++){
for(int j = 0;j < 3;j++){
printf("%d ",arr[i][j]);
}
printf("\n");
}
return 0;
}
练习:
有一个3x4的矩阵(元素值不相同),要求输出其中最大值以及它的行号和列号
#include <stdio.h>
int main(){
int a[3][4] = {1,2,3,4,1,0,76,4,34,2,34,56};
int max = a[0][0];
int hang,lie;
for(int i = 0;i < 3;i++){
for(int j = 0;j < 4;j++){
if(max < a[i][j]){
max = a[i][j];
hang = i + 1;
lie = j + 1;
}
}
}
printf("max = %d hang = %d lie = %d\n",max,hang,lie);
return 0;
}
【5】指针
指针就是地址
地址:内存空间每一个单元格的编号(门牌号)
指针:就是地址,内存地址
指针变量:存储指针的变量
1》指针变量
格式:存储类型 数据类型 * 指针变量名
int *p; //定义了一个指针变量p
int a = 10;
int *p = &a; //p中存储的是a的地址
访问值:*p指针变量名 *p
修改值:*指针变量名 = 新值
int a = 10;
int *p = &a;
printf("%d\n",*p); //访问指针变量的值
*p = 200; //修改指针变量的值
printf("%d\n",*p);
指针和变量之间的关系
指针就是地址
指针变量也还是一个变量,在定义创建一个指针变量时,它也会开辟一个内存空间去存储这个变量
当你将 a 的地址赋给指针变量 p 时,指针变量 p 存的就是 a 的地址,变量 a 里面存的是它的值 10
这两个变量他们都有自己的内存空间
如果你再对指针变量 p 进行取地址 &p 那么得到的就是指针变量 p 的内存地址
2》指针操作符
* :取内容(解引用)
& :取地址
单目运算:从右往左
int a = 10;
*&a; //10 * &两者互逆
&*a; //错误
3》初始化
1)将普通变量赋值给指针
int a=20;
int *p=&a;//定义时赋值
int *p;//未赋值 野指针
int *p=NULL;
int b=10;
p=&b;//先定义后赋值
2)将数组的地址赋值给指针
char s[]="hello";
char *p=s;
3)将指针变量的值交给另一个指针变量
int a=20;
int *p=&a;
int *q=p;
练习:
编写一个程序,找出1000以内所有的完数,所谓完数就是一个数恰好等于它的因子之和,
例如6=1+2+3,6就是一个完数。28也是 28=1+2+4+7+14。
#include <stdio.h>
int main(){
int i,j;
for(i = 1;i <= 1000;i++){
int a = 0;
for(j = 1;j < i;j++){
if(i % j == 0){
a += j;
}
}
if(i == a){
printf("%d ",a);
}
}
return 0;
}