提示:排序和查找算法是算法领域中最基本的概念之一,它们在数据组织、优化查询效率等方面发挥着至关重要的作用。
目录
前言
12.1 排序算法的介绍
12.2 冒泡排序
12.2.1 基本介绍
12.2.2 冒泡排序应用实例
12.2.3 分析冒泡的过程+代码
12.3 查找
12.3.1 介绍
12.3.2 案例演示
12.4 多维数组-二维数组
12.5 二维数组的应用场景
12.6 二维数组的使用
12.6.1 快速入门案例:
12.6.2 使用方式 1: 先定义在初始化
12.6.3 使用演示
12.6.4 使用方式 2: 直接初始化
12.7 二维数组的应用案例
12.7.1 案例 1
12.7.2 案例 2:
12.7.3 前面两个案例的代码
12.8 二维数组使用细节和注意事项
总结
附录
前言
在计算机科学中,算法是解决问题和执行任务的核心工具。排序和查找算法是算法领域中最基本的概念之一,它们在数据组织、优化查询效率等方面发挥着至关重要的作用。本章将深入探讨排序和查找算法的基本原理、实现方式以及它们的应用场景。同时,我们还将介绍二维数组的概念和使用,这是处理多维数据和构建复杂数据结构的基础。通过本章的学习,读者将能够理解并实现这些基本算法,为进一步探索更高级的算法和数据结构打下坚实的基础。
12.1 排序算法的介绍
排序也称排序算法(Sort Algorithm),排序是将一组数据,依指定的顺序进行排列的过程。
排序的分类:
1) 内部排序:
指将需要处理的所有数据都加载到内部存储器(内存)中进行排序。
2) 外部排序法:
数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。
12.2 冒泡排序
12.2.1 基本介绍
冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。
因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志 flag 判断元素是否进行过交换。从而减少不必要的比较。
12.2.2 冒泡排序应用实例
我们举一个具体的案例来说明冒泡法。我们将五个无序的数:{3, 9, -1, 10, -2} 使用冒泡排序法将其排成一个从小到大的有序数列。
12.2.3 分析冒泡的过程+代码
![858fa8f16f2346cabfadf56242633d67.png](https://i-blog.csdnimg.cn/direct/858fa8f16f2346cabfadf56242633d67.png)
#include <stdio.h>
//冒泡排序的函数
void bubbleSort(int arr[], int arrLen) {
//因为每轮排序几乎一样,因此,我们可以使用 for 循环处理
int j,i;
int t;//临时变量
for(i=0; i < arrLen - 1; i++) {
for(j = 0; j < arrLen-1-i; j++) {
//如果前面的数大于后面的数,就交换
if(arr[j] > arr[j+1]) {
t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
}
}
}
}
void main() {
int arr[] = {3, 9, -1, 10, -2,-11} ;
int arrLen = sizeof(arr) / sizeof(int); // 通过计算得到
int j;
bubbleSort(arr, arrLen); // 数组默认是地址传递(指针)
printf("\n 排序后(函数)\n");
for(j= 0; j < arrLen; j++) {
printf("%d ", arr[j]);
}
//第一轮的排序
/*
int j;
int t;//临时变量
for(j = 0; j < 4; j++) {
//如果前面的数大于后面的数,就交换
if(arr[j] > arr[j+1]) {
t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
}
}
//输出看看第 1 轮的排序后的情况
for(j= 0; j < 5; j++) {
printf("%d ", arr[j]);
}
//第 2 轮的排序
for(j = 0; j < 3; j++) {
//如果前面的数大于后面的数,就交换
if(arr[j] > arr[j+1]) {
t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
}
}
//输出看看第 2 轮的排序后的情况
printf("\n");
for(j= 0; j < 5; j++) {
printf("%d ", arr[j]);
}
//第 3 轮的排序
for(j = 0; j < 2; j++) {
//如果前面的数大于后面的数,就交换
if(arr[j] > arr[j+1]) {
t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
}
}
//输出看看第 3 轮的排序后的情况
printf("\n");
for(j= 0; j < 5; j++) {
printf("%d ", arr[j]);
}
//第 4 轮的排序
for(j = 0; j < 1; j++) {
//如果前面的数大于后面的数,就交换
if(arr[j] > arr[j+1]) {
t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
}
}
//输出看看第 3 轮的排序后的情况
printf("\n");
for(j= 0; j < 5; j++) {
printf("%d ", arr[j]);
}*/
getchar();
}
|
12.3 查找
12.3.1 介绍
在 C 中,我们常用的查找有两种:
1) 顺序查找
2) 二分查找
12.3.2 案例演示
1) 有一个数列:{23, 1, 34,89, 101}
猜数游戏:从键盘中任意输入一个数,判断数列中是否包含该数【顺序查找】 要求: 如果找到了,就提示找到,并给出下标值, 找不到提示 没有。
#include <stdio.h>
int seqSearch(int arr[], int arrLen, int val) {
int i;
for(i = 0; i < arrLen; i++) {
if(arr[i] == val) {
return i;
}
}
//如果在 for 循环中,没有执行到 return ,说明没有找到
return -1;
}
void main() {
//有一个数列:{23, 1, 34,89, 101}
//猜数游戏:从键盘中任意输入一个数,判断数列中是否包含该数【顺序查找】 要求: 如果找到了,
//就提示找到,并给出下标值, 找不到提示 没有。
//分析思路
//1. 安装数组进行遍历,一个一个的比较,如果相等,则找到
int arr[] = {23, 1, 34,89,101};
int arrLen = sizeof(arr) / sizeof(int);
int index = seqSearch(arr, arrLen, -101);
if (index != -1) { //找到
printf("找到 下标为 %d", index);
} else {
printf("没有找到");
}
getchar();
}
|
2) 请对一个有序数组进行二分查找 {1,8, 10, 89, 1000, 1234} ,输入一个数看看该数组是否存在此数,并且求出下标,如果没有就提示"没有这个数"。
二分查找的前提是,该数组是一个有序数组
#include <stdio.h>
//二分查找
int binarySearch(int arr[], int leftIndex, int rightIndex, int findVal) {
//先找到数组中间这个数 midVal
int midIndex = (leftIndex + rightIndex) / 2;
int midVal = arr[midIndex];
//如果 leftIndex > rightIndex, 说明这个数组都比较过,但是没有找到
if( leftIndex > rightIndex) {
return -1;//!!!!
}
//如果 midVal > findVal 说明, 应该在 midVal 的左边查找
if(midVal > findVal) {
binarySearch(arr, leftIndex, midIndex-1, findVal);//!!!
} else if(midVal < findVal){//如果 midVal < findVal 说明, 应该在 midVal 的右边查找
binarySearch(arr, midIndex+1, rightIndex, findVal);//!!!
} else {
return midIndex; //返回该数的下标
}
}
void main() {
//请对一个有序数组进行二分查找 {1,8, 10, 89, 1000, 1234} ,
//输入一个数看看该数组是否存在此数,并且求出下标,
//如果没有就提示"没有这个数"。
二分查找的前提是,该数组是一个有序数组
//思路分析
// 比如我们要查找的数是 findVal
//1. 先找到数组中间这个数 midVal,和 findVal 比较
//2. 如果 midVal > findVal 说明, 应该在 midVal 的左边查找
//3. 如果 midVal < findVal 说明, 应该在 midVal 的右边查找
//4. 如果 midVal == findVal, 说明找到
//5. 这里还有一个问题,没有考虑找不到情况->
int arr[] = {1,8, 10, 89, 1000, 1234};
int arrLen = sizeof(arr) / sizeof(int);
int index = binarySearch(arr, 0, arrLen-1, -1000);
if(index != -1) {
printf("找到 index = %d", index);
} else {
printf("没有找到");
}
getchar();
}
|
12.4 多维数组-二维数组
多维数组我们介绍二维数组
12.5 二维数组的应用场景
比如我们开发一个五子棋游戏,棋盘就是需要二维数组来表示。如图:
![3bdb155f953544c09cdde8f1dccac3b4.png](https://i-blog.csdnimg.cn/direct/3bdb155f953544c09cdde8f1dccac3b4.png)
12.6 二维数组的使用
12.6.1 快速入门案例:
请用二维数组输出如下图形
0 0 0 0 0 0
0 0 1 0 0 0
0 2 0 3 0 0
0 0 0 0 0 0
12.6.2 使用方式 1: 先定义在初始化
语法: 类型 数组名[大小][大小];
比如: int a[2][3];
12.6.3 使用演示
二维数组在内存的存在形式,各个元素的地址是连续分布的,即在前一个元素基础上+4
代码演示:
#include <stdio.h>
void main() {
//a[4][6] : 表示一个 4 行 6 列的二维数组
int a[4][6]; // 没有初始化,则是分配的内存垃圾值
int i, j;
//全部初始化 0
for(i = 0; i < 4; i++) { //先遍历行
for(j = 0; j < 6; j++) {//遍历列
a[i][j] = 0;
}
}
a[1][2] = 1;
a[2][1] = 2;
a[2][3] = 3;
//输出二维数组
for(i = 0; i < 4; i++) {
for(j = 0; j < 6; j++) {
printf("%d ", a[i][j]);
}
printf("\n");
}
看看二维数组的内存布局
printf("\n 二维数组 a 的首地址=%p", a);
printf("\n 二维数组 a[0]的地址=%p", a[0]);
printf("\n 二维数组 a[0][0]的地址=%p", &a[0][0]);
printf("\n 二维数组 a[0][1]的地址=%p", &a[0][1]);
//将二维数组的各个元素得地址输出
printf("\n");
for(i = 0; i < 4; i++) {
printf("a[%d]的地址=%p ", i, a[i]);
for(j=0; j < 6; j++) {
printf("a[%d][%d]的地址=%p ", i, j , &a[i][j]);
}
printf("\n");
}
getchar();
}
|
Ø 画出了二维数组的内存布局图!!!
![1cfa4bd3d93d4fa1b4ec6426f0655c1a.png](https://i-blog.csdnimg.cn/direct/1cfa4bd3d93d4fa1b4ec6426f0655c1a.png)
12.6.4 使用方式 2: 直接初始化
1) 定义 类型 数组名[大小][大小] = {{值 1,值 2..},{值 1,值 2..},{值 1,值 2..}};
2) 或者 类型 数组名[大小][大小] = { 值 1,值 2,值 3,值 4,值 5,值 6 ..};
![8bedd4689e914248a76c10dda9644b92.png](https://i-blog.csdnimg.cn/direct/8bedd4689e914248a76c10dda9644b92.png)
12.7 二维数组的应用案例
12.7.1 案例 1
请使用灵活的方式遍历如下数组 :
int map[3][3] = {{0,0,1},{1,1,1},{1,1,3}};
12.7.2 案例 2:
int arr[3][2]={{4,6},{1,4},{-2,8}};
遍历该二维数组,并得到和?
12.7.3 前面两个案例的代码
#include <stdio.h>
void main() {
int map[3][3] = {{0,0,1},{1,1,1},{1,1,3}};
//遍历
//先得到行
//1. sizeof(map) 得到这个 map 数组的大小 9 * 4 = 36
//2. sizeof(map[0]) 得到 map 中,第一行有多大 3 * 4 = 12
int rows = sizeof(map) / sizeof(map[0]); // 3
/*printf("rows=%d", rows);*/
//得到列
int cols = sizeof(map[0]) / sizeof(int); // 12 / 4 = 3
int i,j, sum=0;
for(i = 0; i < rows; i++) {
for(j = 0; j < cols; j++) {
printf("%d ", map[i][j]);
sum += map[i][j];//累计到 sum
}
printf("\n");
}
printf("\n sum=%d", sum);
getchar();
}
|
12.7.4 定义二维数组,用于保存三个班,每个班五名同学成绩,并求出每个班级平均分、以及所有班级平均分
#include <stdio.h>
void main() {
/*
定义二维数组,用于保存三个班,
每个班五名同学成绩,并求出每个班级平均分、以及所有班级平均分
分析
1. 创建一个 scores[3][5]
2. 遍历,给赋值
3. 再次遍历,统计总分和平均分
4. 输出
*/
double score[3][5]; //
int rows = sizeof(score) / sizeof(score[0]), cols = sizeof(score[0])/sizeof(double), i, j; //
//classTotalScore 各个班级总成绩 totalScore 所有学生成绩
double totalScore = 0.0, classTotalScore = 0.0;
for (i = 0; i < rows; i++ ) {
for (j = 0; j < cols ; j++ ) {
score[i][j] = 0.0; //初始化
}
}
//遍历,给每个学生输入成绩
for (i = 0; i < rows; i++ ) {
for (j = 0; j < cols ; j++ ) {
printf("请输入第 %d 个班的 第 %d 个 学生的成绩", i + 1, j + 1);
scanf("%lf", &score[i][j]);
}
}
//getchar();
//显示下成绩情况
for (i = 0; i < rows; i++ ) {
for (j = 0; j < cols ; j++ ) {
printf("%.2f ",score[i][j]);
}
printf("\n");
}
//统计各个班的总成绩,和所有学生的总成绩
for (i = 0; i < rows; i++ ) {
classTotalScore = 0.0; // 每次清 0
for (j = 0; j < cols ; j++ ) {
classTotalScore += score[i][j]; //累计每个班的总成绩
}
printf("\n 第 %d 个班的平均成绩是 %.2f" , i+1, classTotalScore/cols );
totalScore += classTotalScore; //将该班级的总分,累计到 totalScore
}
printf("\n 所有学生总成绩是 %.2f 平均成绩 %.2f" , totalScore, totalScore/(rows * cols));
getchar();
getchar();
}
|
12.8 二维数组使用细节和注意事项
1) 可以只对部分元素赋值,未赋值的元素自动取“零”值【案例】
int main(){
int a[4][5] = {{1}, {2}, {3},{1}};
int i,j;
for (i = 0; i < 4; i++ ) {
for (j = 0; j < 5 ; j++ ) {
printf("%d ",a[i][j]);
}
printf("\n");
}
getchar();
}
|
2) 如果对全部元素赋值,那么第一维的长度可以不给出。比如:
int a[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
可以写为:
int a[][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
|
3) 二维数组可以看作是由一维数组嵌套而成的;如果一个数组的每个元素又是一个数组,那么它就是二维数组。
二维数组 a[3][4]可看成三个一维数组,它们的数组名分别为 a[0]、a[1]、a[2]。
这三个一维数组都有 4 个元素,如,一维数组 a[0] 的元素为 a[0][0]、a[0][1]、a[0][2]、a[0][3]
|
总结
通过本章的学习,我们掌握了排序算法的基本概念,包括内部排序和外部排序的区别,以及冒泡排序的具体实现方法。我们了解了查找算法中的顺序查找和二分查找,并学习了它们在不同场景下的应用。此外,本章还详细介绍了二维数组的使用方法,包括定义、初始化、内存布局以及在实际问题中的应用案例。我们认识到了二维数组在实际编程中的重要性,以及如何通过二维数组解决多维数据处理问题。最后,我们总结了二维数组使用时的一些细节和注意事项,帮助读者在实际编程中避免常见错误,提高代码的准确性和效率。
附录
参考:【尚硅谷C语言零基础快速入门教程-哔哩哔哩】 https://b23.tv/vS3vTDp