【数据结构初阶第十八节】八大排序系列(上篇)—[详细动态图解+代码解析]

看似不起眼的日复一日,总会在某一天让你看到坚持的意义。​​​​​​云边有个稻草人-CSDN博客

hello,好久不见! 

目录

一. 排序的概念及运用

1. 概念

2. 运用 

3. 常见排序算法

二. 实现常见排序算法

1. 插入排序

(1)直接插入排序

【图解】

【代码】

【直接插入排序的特性总结】

【冒泡排序,堆排序,直接插入排序时间复杂度比较】

(2)希尔排序

【思路代码图解】 

【优化】

【希尔排序代码】

【希尔排序时间复杂度】

2.选择排序

(1)直接选择排序

【代码】

【直接选择排序的特性总结】

(2)堆排序

3.交换排序

(1)冒泡排序

【代码实现】

【冒泡排序特性总结】

(2)快速排序

【Hoare版本】

【细节问题】

【代码】

【代码图解】

Relaxing Time !

————————————《We Don't Talk Anymore》————————————


正文开始——

一. 排序的概念及运用

1. 概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

2. 运用 

院校排名

3. 常见排序算法

接下来我们来学习实现这些常见的排序算法。。。

二. 实现常见排序算法

int a[] = {5, 3, 9, 6, 2, 4, 7, 1, 8};

1. 插入排序

【基本思想】

直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。

实际中我们玩扑克牌时,就用了插入排序的思想。

(1)直接插入排序
【图解】
当插入第 i (i>=1) 个元素时,前面的 array[0],array[1],…,array[i-1] 已经排好序,此时用 array[i] 的排序码与 array[i-1],array[i-2],… 的排序码顺序进行比较,找到插入位置即将 array[i] 插入 ,原来位置上的元素顺序后移。

这个动图显示了直接插入排序的步骤,里面原始的数据是10,9,8,7,6,5,4,3,2,1,是一个降序序列,现在我们用直接插入排序将其排列成升序序列,细节过程难以用文字描述,我们对照着代码理解。

  • 外层 for 循环来控制内层循环的次数。
  • for 循环的 i 的最大值为n-2,最后一次循环end指向倒数第二个元素,此时tmp里面存放的是整个序列里面最后一个数据,就是再将最后一个数据插入到前面有序序列里面合适的位置整体就ok了。
  • 每一次进入新的for循环就是新的end,新的tmp,各自又分配到了新的数据,重复前面整体的步骤;进入到里面的 while 循环是在为 tmp 在前面的有序序列里面找合适的位置插入进去。
  • 每次插入数据都是往end+1的地方插,end+1这个位置留用,谁大谁往这放。
  • tmp保存正在排序的数据,不断比较然后赋值的过程中会将tmp原位置的值给覆盖,所以我们保存一份在找到合适的位置后给它放进去。
【代码】
//直接插入排序
void InsertSort(int* arr, int n)
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = arr[end + 1];while (end >= 0){if (arr[end] > tmp){arr[end + 1] = arr[end];end--;//--是为了让tmp与区间前面的元素进行挨个比较,最后找到合适的位置}else{break;}}//此时end可能等于-1,或者是因为end指向的元素 < tmp,此时我们还没有进行最后的赋值,//这时我们都要把tmp里面的数据赋值给arr[end+1]arr[end + 1] = tmp;}
}
【直接插入排序的特性总结】

【冒泡排序,堆排序,直接插入排序时间复杂度比较】

冒泡排序代码见下

时间复杂度为O(N^2),空间复杂度为O(1)


void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}//冒泡排序
void BubbleSort(int* arr, int n)
{for (int i = 0; i < n; i++){int exchange = 0;for (int j = 0; j < n - i - 1; j++){//升序if (arr[j] > arr[j + 1]){exchange = 1;Swap(&arr[j], &arr[j + 1]);}}if (exchange == 0){break;}}
}

堆排序代码见下

时间复杂度为O(N * logN),空间复杂度为O(1)

//向下调整数据
void AdjustDown(int* arr, int parent, int n)
{int child = parent * 2 + 1;while (child < n){//找出左右孩子中最小的->小堆 >//找出左右孩子中最大的->大堆 <if (child + 1 < n && arr[child] < arr[child + 1]){child++;}// < 建小堆// > 建大堆 if (arr[child] > arr[parent]){Swap(&arr[parent], &arr[child]);parent = child;child = parent * 2 + 1;}else{break;}}
}//堆排序
void HeapSort(int* arr, int n)
{//向下调整算法建堆for (int i = (n - 2) / 2; i >= 0; i--){AdjustDown(arr, i, n);}//堆排序int end = n - 1;while (end > 0){//end指向的是最后一个数据Swap(&arr[0], &arr[end]);//有效的数据个数减了1AdjustDown(arr, 0, end);end--;}
}

直接插入排序代码见下

时间复杂度最好的情况是O(N),最差的情况为O(N^2),最差的情况只有是有序的降序序列时才发生(如果我们要排升序的话)

//直接插入排序
void InsertSort(int* arr, int n)
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = arr[end + 1];while (end >= 0){if (arr[end] > tmp){arr[end + 1] = arr[end];end--;//--是为了让tmp与区间前面的元素进行挨个比较,最后找到合适的位置}else{break;}}//此时end可能等于-1,或者是因为end指向的元素 < tmp,此时我们还没有进行最后的赋值,//这时我们都要把tmp里面的数据赋值给arr[end+1]arr[end + 1] = tmp;}
}

光说不行,我们可以借助测试代码来验证这几种排序的性能到底如何——

TestOP函数思路——我们先开辟10W个数据类型大小的空间,然后随机生成10W个数据并将数据依次插入到数组里面,这里面采用了赋值,保证各个数组里面的数据是一样的,这样在利用同样数据的数组排序的时候就会公平统一,之后利用clock函数来计算各个排序运行的时间。下面是clock函数的简单介绍,使用起来还是很简单的,对于TestOP()里面的使用,就是求两次clock函数之间的时间差就是排序所用的时间。

// 测试排序的性能对⽐
//下面我们还没有实现的排序求时间我们就先注释掉
void TestOP()
{srand(time(0));const int N = 100000;int* a1 = (int*)malloc(sizeof(int) * N);/*int* a2 = (int*)malloc(sizeof(int) * N);int* a3 = (int*)malloc(sizeof(int) * N);*/int* a4 = (int*)malloc(sizeof(int) * N);/*int* a5 = (int*)malloc(sizeof(int) * N);int* a6 = (int*)malloc(sizeof(int) * N);*/int* a7 = (int*)malloc(sizeof(int) * N);for (int i = 0; i < N; ++i){a1[i] = rand();/*a2[i] = a1[i];a3[i] = a1[i];*/a4[i] = a1[i];/*a5[i] = a1[i];a6[i] = a1[i];*/a7[i] = a1[i];}int begin1 = clock();InsertSort(a1, N);int end1 = clock();/*int begin2 = clock();ShellSort(a2, N);int end2 = clock();int begin3 = clock();SelectSort(a3, N);int end3 = clock();*/int begin4 = clock();HeapSort(a4, N);int end4 = clock();//int begin5 = clock();//QuickSort(a5, 0, N - 1);//int end5 = clock();//int begin6 = clock();//MergeSort(a6, N);//int end6 = clock();int begin7 = clock();BubbleSort(a7, N);int end7 = clock();printf("InsertSort:%d\n", end1 - begin1);/*printf("ShellSort:%d\n", end2 - begin2);printf("SelectSort:%d\n", end3 - begin3);*/printf("HeapSort:%d\n", end4 - begin4);/*printf("QuickSort:%d\n", end5 - begin5);printf("MergeSort:%d\n", end6 - begin6);*/printf("BubbleSort:%d\n", end7 - begin7);free(a1);/*free(a2);free(a3);*/free(a4);/*free(a5);free(a6);*/free(a7);
}

下面我们来看一下运行结果

运行结果的单位是ms,对于相同的10W数据直接插入排序时间是3s多,而堆排序还要小得多,但是冒泡排序是接近40s,可见冒泡排序的效率之低(冒泡排序的作用仅仅只是教学意义,让一开始菜鸟的我学习循环嵌套啥的)。堆排序效率确实很高,直接插入排序也还不错,下面我们可以再对直接插入排序进行优化,就变成了希尔排序

(2)希尔排序
希尔排序法又称 缩小增量法 。希尔排序法的基本思想是:先选定⼀个整数(通常是gap = n/3+1),把待排序文件所有记录分成各组,所有的距离相等的记录分在同⼀组内,并对每⼀组内的记录进行排序,然后gap=gap/3+1得到下⼀个整数,再将数组分成各组,进⾏插入排序,当gap=1时,就相当于直接插入排序。 它是在直接插⼊排序算法的基础上进⾏改进而来的,综合来说它的效率肯定是要⾼于直接插⼊排序算法的。下图是大致的排序思路

【思路代码图解】 

【优化】

注意gap的变化,gap = gap / 3 +1。

【希尔排序代码】
//希尔排序
//时间复杂度为O(N^1.3)
void ShellSort(int* arr, int n)
{int gap = n;//最外层循环来控制每组元素的间隔while (gap > 1){gap = gap / 3 + 1;for (int i = 0; i < n - gap; i++){int end = i;int tmp = arr[end + gap];while (end >= 0){if (arr[end] > tmp){arr[end + gap] = arr[end];end -= gap;}else{break;}}arr[end + gap] = tmp;}}
}

我们还是调用上面的测试代码来看一下运行时间,结果显示希尔排序相对于直接插入排序确实提高了不少,赞!

下去我们可以试着测试,对于同样的降序序列,直接插入排序和希尔排序各自的运行效率怎么样,可以先使用冒泡排序搞一个降序序列。

【希尔排序时间复杂度】

经过不断地预排序,小的数据基本在左边,大的数据基本在右边,在以后gap==1时,数组已经接近有序,时间复杂度不可能达到n^2,应该是接近O(n)。总的下来希尔排序的时间复杂度接近O(n^1.3)。

为什么gap = gap / 3 + 1取3个为一组呢?假设有10个数据,我们看一下取3个为一组和2个为一组的区别:

gap / 3 + 1     4---->2---->1

gap / 2 + 1     6---->4---->3---->2---->1

显而易见,当取三个为一组的话外层循环的次数比较少的就可以达到gap==1,两种方法相比每组分到的数据个数相差不大,循环次数越多,时间复杂度就越高

通过上面的分析我们可以画出下面的曲线图

因此,希尔排序在最初和最后的排序的次数都为n,即前⼀阶段排序次数是逐渐上升的状态,当到达某⼀顶点时,排序次数逐渐下降⾄n,⽽该顶点的计算暂时⽆法给出具体的计算过程。
希尔排序时间复杂度不好计算,因为 gap 的取值很多,导致很难去计算,因此很多书中给出的希尔排序的时间复杂度都不固定。《数据结构(C语⾔版)》--- 严蔚敏书中给出的时间复杂度为:

2.选择排序

基本思想
每⼀次从待排序的数据元素中选出最⼩(或最⼤)的⼀个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
(1)直接选择排序
  •  在元素集合 array[i]--array[n-1] 中选择关键码最⼤(⼩)的数据元素;
  •  若它不是这组元素中的最后⼀个(第⼀个)元素,则将它与这组元素中的最后⼀个(第⼀个)元素;
  • 在剩余的 array[i]--array[n-2] array[i+1]--array[n-1] ) 集合中,重复上述步骤,直到集合剩余 1 个元素。

【代码】
//直接插入排序
void SelectSort(int* arr, int n)
{int begin = 0;int end = n - 1;while (begin < end){int mini = begin, maxi = begin;//在后面区间内找maxi,minifor (int j = begin+1; j <= end; j++){if (arr[j] < arr[mini]){mini = j;}if (arr[j] > arr[maxi]){maxi = j;}}if (maxi == begin){maxi = mini;}Swap(&arr[begin], &arr[mini]);Swap(&arr[end], &arr[maxi]);begin++;end--;}
}

看一下运行结果是否正确,对了!

【直接选择排序的特性总结】
  • 直接选择排序比较好理解,但是效率不好,实际中很少使用,那为啥还要学呢,为了你知道这是最差的排序方式(哈哈)
  • 时间复杂度:O(N^2)
  • 空间复杂度:O(1)

(2)堆排序
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的⼀种排序算法,它是选择排序的⼀种。它是通过堆来进行选择数据。需要注意的是排升序要建⼤堆,排降序建⼩堆。在⼆叉树章节我们已经实现过堆排序,这里不再赘述,我之前写了一篇详细的关于堆排序的博客,下面是博客链接:
【数据结构初阶第十五节】堆的应用(堆排序 + Top-K问题)-CSDN博客

3.交换排序

(1)冒泡排序
前⾯在算法题中我们已经接触过冒泡排序的思路了,冒泡排序是⼀种最基础的交换排序。之所以叫做冒泡排序,因为每⼀个元素都可以像小气泡⼀样,根据自身大小⼀点⼀点向数组的⼀侧移动。

【代码实现】
//冒泡排序
void BubbleSort(int* arr, int n)
{for (int i = 0; i < n; i++){int exchange = 0;for (int j = 0; j < n - i - 1; j++){if (arr[j] < arr[j + 1]){exchange = 1;Swap(&arr[j], &arr[j + 1]);}}if (exchange == 0){break;}}
}
【冒泡排序特性总结】

时间复杂度:O(N^2)

空间复杂度:O(1)

(2)快速排序
快速排序是Hoare于1962年提出的⼀种⼆叉树结构的交换排序⽅法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两⼦序列,左⼦序列中所有元素均⼩于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
快速排序实现主框架:
//快排
void QuickSort(int* arr, int left, int right)
{if (left >= right){return;}//1.找基准值int key = _QuickSort(arr,left,right);//2.左子序列进行排序QuickSort(arr, left, key - 1);//3.右子序列进行排序QuickSort(arr, key + 1, right);
}

 将区间中的元素进⾏划分的 _QuickSort ⽅法主要有以下⼏种实现⽅式:

【Hoare版本】
【细节问题】

【代码】
//找基准值
int _QuickSort(int* arr, int left, int right)
{int key = left;left++;while (left <= right){//找比基准值大的while (left <= right && arr[left] < arr[key]){left++;}//找比基准值小的while (left <= right && arr[right] > arr[key]){right--;}if (left <= right){Swap(&arr[right--], &arr[left++]);}}//将right指向的数据和key指向的数据进行交换Swap(&arr[key], &arr[right]);return right;
}//快排
void QuickSort(int* arr, int left, int right)
{if (left >= right){return;}//1.找基准值int key = _QuickSort(arr,left,right);//2.左子序列进行排序QuickSort(arr, left, key - 1);//3.右子序列进行排序QuickSort(arr, key + 1, right);
}

效果展示:

我们再根据代码来一遍流程,看看代码内部到底是怎么运行的

【代码图解】

问题1:为什么跳出循环后right位置的值⼀定不⼤于key?
left > right 时,即right⾛到left的左侧,⽽left扫描过的数据均不⼤于key,因此right此时指向的数据⼀定不⼤于key
问题2:为什么left 和 right指定的数据和key值相等时也要交换?
相等的值参与交换确实有⼀些额外消耗。实际还有各种复杂的场景,假设数组中的数据⼤量重复时,⽆法进⾏有效的分割排序。

对于同样的10W个数据,我们来看一下这几个排序时间,见下:

 

我们把数据上调到100W个,来看一下这几个排序的时间,快排确实有点东西

今天就先学那么些,剩下的明天继续更!

完——


Relaxing Time !

————————————《We Don't Talk Anymore》————————————

 “为你,千千万万遍” ——哈桑

We Don't Talk Anymore_Charlie Puth、Selena Gomez_高音质在线试听_We Don't Talk Anymore歌词|歌曲下载_酷狗音乐

至此结束——

我是云边有个稻草人

期待与你的下一次相遇!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/71718.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python爬虫系列课程8:js浏览器window对象属性

python爬虫系列课程8:js浏览器window对象属性 一、JavaScript的组成二、document常见属性对象三、navigator对象一、JavaScript的组成 JavaScript可以分为三个部分:ECMAScript标准、DOM、BOM。 ECMAScript标准:即JS的基本语法,JavaScript的核心,描述了语言的基本语法和数…

快速使用PPASR V3版不能语音识别框架

前言 本文章主要介绍如何快速使用PPASR语音识别框架训练和推理&#xff0c;本文将致力于最简单的方式去介绍使用&#xff0c;如果使用更进阶功能&#xff0c;还需要从源码去看文档。仅需三行代码即可实现训练和推理。 源码地址&#xff1a;https://github.com/yeyupiaoling/P…

cannon g3810打印机设置

现在AI这么厉害&#xff0c;是不是很少人来这里搜索资料了。 不过我还是写一下。 买了一台cannon g3810打印机。一直都用USB打印&#xff0c;今天突然想用手机打印。于是又折腾了两个小时&#xff0c;终于折腾完了。 步骤如下&#xff1a; [1]打开官网&#xff0c;下载佳能…

使用 Arduino 和 ThingSpeak 通过 Internet 进行心跳监测

使用 Arduino 和 ThingSpeak 通过 Internet 进行心跳监测 在这个项目中,我们将使用 Arduino 制作一个心跳检测和监测系统,该系统将使用脉搏传感器检测心跳,并在与其连接的 LCD 上显示 BPM(每分钟心跳次数)读数。它还将使用 Wi-Fi 模块ESP8266将读数发送到 ThingSpeak 服务…

vulnhub靶场之【digitalworld.local系列】的snakeoil靶机

前言 靶机&#xff1a;digitalworld.local-snakeoil&#xff0c;IP地址为192.168.10.11 攻击&#xff1a;kali&#xff0c;IP地址为192.168.10.6 kali采用VMware虚拟机&#xff0c;靶机选择使用VMware打开文件&#xff0c;都选择桥接网络 这里官方给的有两种方式&#xff0…

自行车的主要品牌

一、国际知名品牌&#xff08;专注运动与高端市场&#xff09; 捷安特&#xff08;GIANT&#xff09; 台湾品牌&#xff0c;全球最大自行车制造商之一&#xff0c;覆盖山地车、公路车、通勤车等多品类。 美利达&#xff08;MERIDA&#xff09; 台湾品牌&#xff0c;以山地车…

C语言(队列)

1、队列的原理和作用 1、1 队列的原理 队列的原理其实就像一个管道&#xff0c;如果我们不断的往管道里塞乒乓球&#xff0c;每个乒乓球在管道里就会排列一条队列&#xff0c;先进去的乒乓球会先出来&#xff0c;这个就是队列先进先出的规则 球从左边进去的动作叫入列&#xf…

【C++算法】AVL树的平衡之美:从理论到C++高效实现

AVL树是一种自平衡二叉搜索树,解决了普通二叉搜索树在数据倾斜时的性能退化问题。本文深入探讨了AVL树的理论基础,包括平衡因子的定义、旋转操作的数学推导,并通过LaTeX公式分析其时间复杂度。接着,我们用C++实现了一个完整的AVL树,包括插入、删除和平衡调整的详细代码,附…

黑金风格人像静物户外旅拍Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色教程 针对人像、静物以及户外旅拍照片&#xff0c;运用 Lightroom 软件进行风格化调色工作。旨在通过软件中的多种工具&#xff0c;如基本参数调整、HSL&#xff08;色相、饱和度、明亮度&#xff09;调整、曲线工具等改变照片原本的色彩、明度、对比度等属性&#xff0c;将…

ESP8266 NodeMCU 与 Atmega16 微控制器连接以发送电子邮件

NodeMCU ESP8266 AVR 微控制器 ATmega16 的接口 Atmega16 是一款低成本的 8 位微控制器,比以前版本的微控制器具有更多的 GPIO。它具有所有常用的通信协议,如 UART、USART、SPI 和 I2C。由于其广泛的社区支持和简单性,它在机器人、汽车和自动化行业有广泛的应用。 Atmega1…

【Hadoop】详解HDFS

Hadoop 分布式文件系统(HDFS)被设计成适合运行在通用硬件上的分布式文件系统&#xff0c;它是一个高度容错性的系统&#xff0c;适合部署在廉价的机器上&#xff0c;能够提供高吞吐量的数据访问&#xff0c;非常适合大规模数据集上的应用。为了做到可靠性&#xff0c;HDFS创建了…

2025 批量下载市场高标解读/配置喵/wangdizhe 雪球帖子/文章导出excel和pdf

之前分享过文章2025 批量下载雪球和东方财富文章导出excel和pdf &#xff0c;今天整理分享下我下载过的一些雪球文章。 第1个号市场高标解读 抓取下载的所有帖子excel数据包含文章日期&#xff0c;文章标题&#xff0c;文章链接&#xff0c;文章简介&#xff0c;点赞数&#…

2022年《申论》第二题(河北A卷)

材料&#xff1a; “社区很大&#xff0c;共有安置房148栋&#xff0c;安置人口2.9万人。人员众多&#xff0c;而且原来都来自农村&#xff0c;群众生活环境变化大&#xff0c;不适应。”春林易地搬迁安置点建成使用后&#xff0c;老单便来这里担任春林街道办主任。如何有效治…

Qt中实现多个QMainWindow同时显示

在Qt中实现多个QMainWindow同时显示&#xff0c;可通过以下方法实现&#xff1a; 一、直接显示多个实例 必须使用new创建堆对象&#xff0c;避免栈对象因作用域结束被销毁‌。 int main(int argc, char *argv[]) {QApplication a(argc, argv);// 创建两个独立的主窗口QMainW…

从运动手环到医疗贴片,精密校平机正在重塑柔性电子器件的工业化生产标准

在柔性电子器件的制造领域&#xff0c;从运动手环到医疗贴片&#xff0c;精密校平机的应用正引领一场生产标准的变革。传统的柔性电子器件生产过程中&#xff0c;材料的平整度控制往往不够精确&#xff0c;导致产品质量参差不齐。然而&#xff0c;随着精密校平机的引入&#xf…

AIP-161 域掩码

编号161原文链接AIP-161: Field masks状态批准创建日期2021-03-01更新日期2021-03-01 在&#xff08;使用AIP-134的Update或类似方法&#xff09;更新资源时&#xff0c;通常需要明确指定哪些域需要更新。服务可以忽略另外的域&#xff0c;即使用户发送了值。 定义一种掩码格…

掌握Kubernetes Network Policy,构建安全的容器网络

在 Kubernetes 集群中&#xff0c;默认情况下&#xff0c;所有 Pod 之间都是可以相互通信的&#xff0c;这在某些场景下可能会带来安全隐患。为了实现更精细的网络访问控制&#xff0c;Kubernetes 提供了 Network Policy 机制。Network Policy 允许我们定义一组规则&#xff0c…

Flask 小册子简介

这是一个Flask restful讲解的小册子&#xff0c;涵盖了 RESTful API 的概念、选择 Flask 的原因以及小册子的目标和结构。我会尽量写得详细&#xff0c;帮助你更好地理解。 1. 简介 1.1 什么是 RESTful API&#xff1f; 1.1.1 REST 的概念 REST&#xff08;Representational…

ElementUI 级联选择器el-cascader启用选择任意一级选项,选中后关闭下拉框

1、启用选择任意一级选项 在 el-cascader 标签上加上配置项&#xff1a; :props"{ checkStrictly: true }"例如&#xff1a; <el-cascaderref"selectedArrRef"v-model"selectedArr":options"optionsList":props"{ checkStri…

typedef 和 using 有什么区别?

在 C 编程中&#xff0c;类型别名&#xff08;Type Aliases&#xff09;是为已有类型定义新名称的一种机制&#xff0c;能够显著提升代码的可读性和可维护性。C 提供了两种工具来实现这一功能&#xff1a;传统的 typedef 和 C11 引入的 using 关键字。 概念 类型别名本质上是为…