【数据结构】排序算法

news2024/11/18 1:36:18

目录

1.理解排序 

1.1 排序的概念 

1.2 排序的运用场景

1.3 常见的排序算法 

2.插入排序算法

2.1 直接插入排序

2.2 希尔排序

3.选择排序算法

3.1 直接选择排序

3.2 堆排序 

4.交换排序算法

4.1 冒泡排序

4.2 快速排序 

4.2.1 hoare 法 

4.2.2 挖坑法

4.2.3 前后指针法 

4.2.4 三数取中法 

5.归并排序算法 

6. 排序算法复杂度及稳定性分析


1.理解排序 

1.1 排序的概念 

排序的概念:排序无非就是将一组数据,按照其中某个或者某些关键字的大小,进行从大到小或者从小到大的顺序排列

升序:也称递增,也就是从小到大排序 

降序:也称递减,也就是从大到小排序 

排序按照稳定性可以分为:稳定性排序和不稳定性排序 

  • 稳定性排序:假设我们现在有一组数据,数据中有两个相同的元素。排序完后,这两个相同元素的顺序依旧保持原来的顺序(在前面的那个元素,排序完后依旧在另一个的前面),这就是稳定性排序
  • 不稳定性排序:假设我们现在有一组数据,数据中有两个相同的元素。排序完后,这两个相同元素的顺序可能会发生变化(在前面的那个元素,排序完后可能在另一个元素的后面),这就是不稳定性排序

排序按照排序所在的地方可以分为:内部排序和外部排序

  • 内部排序:数据元素全部放在内存中的排序 
  • 外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序

1.2 排序的运用场景

例1:我们应该每个人都会用QQ和微信这种通信软件,当我们未设置置顶的时候,它会默认按照别人所发消息的时间进行排序 

例2:我们通常都会用淘宝进行购物,当我们点击销量时,它会按照销量进行排序 

1.3 常见的排序算法 

我们常见的排序算法有七个,分别为:直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序

这七个常见的排序,又可以分为四类,分别为: 插入排序、选择排序、交换排序、归并排序

2.插入排序算法

2.1 直接插入排序

直接插入排序:大家应该都玩过扑克牌,玩扑克牌的时候,大家都会一张一张的拿牌,每拿到一张牌我们都会将其插入到合适的位置,这就是直接插入排序

假设我们现在有一个数组为 {5,2,4,6,1,3} ,现在需要采用直接插入排序算法进行排序

直接插入排序思路:通过循环遍历数组让 end 存放数组第一个元素的下标,tmp 存放的是 end 后面一个元素的值。然后内循环判断 end 是否大于等于 0,如果大于等于 0 则进入内循环,若为升序排序,则判断 tmp 中存放的值是否小于 end 下标对应的值,如果时则将 end 下标对应的值放在 end+1 下标位置上(往后移),然后让 end--,若 end<0 就跳出循环, 如果 tmp 中存放的值不小于 end 下标对应的值。跳出内循环后让 end+1 下标对应数组元素等于 tmp。然后使 end 指向第二个元素的下标,同样的方法依次比较。

//直接插入排序
public void insertSortDirectly(int[] arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        int end = i;
        int tmp = arr[end + 1];
        while (end >= 0) {
            if (tmp < arr[end]) {
                arr[end + 1] = arr[end];
                end--;
            } else {
                break;
            }
        }
        arr[end + 1] = tmp;
    }
}

直接插入排序的特性总结:

  • 当元素集合越接近有序,直接插入排序算法的时间效率越高
  • 时间复杂度:O(N^2)
  • 空间复杂度:O(1),它是一种稳定的排序算法
  • 稳定性:稳定

2.2 希尔排序

希尔排序法又称缩小增量法其实相当于直接插入排序的进阶版,它比直接插入排序多了预排序

希尔排序分为两步:预排序(让数据接近有序),直接插入排序(让数据有序)

希尔排序思想:先选定一个整数存放在 gap 变量中,把待排序数据按照 gap 大小分成组,所有距离为 gap 的分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达 gap=1 时,所有数据在统一组内排好序。 

假设我们现在有一个数组为 {5,2,4,6,1,3} ,现在需要采用希尔排序算法进行排序

//希尔排序
public void shellSort(int[] arr) {
    int gap = arr.length;
    //预排序
    while (gap > 1) {
        gap = gap / 2;
        for (int i = 0; i < arr.length - gap; i++) {
            int end = i;
            int tmp = end + gap;
            while (end >= 0) {
                if (tmp < arr[end]) {
                    arr[end + gap] = arr[end];
                    end = end - gap;
                } else {
                    break;
                }
            }
            arr[end + gap] = tmp;
        }
    }
}

gap值应该如何设定?

gap值通常设为数据长度的一半,然后依次除 2。当 gap 不为 1 时都为预排序,当 gap 为 1 时就是直接插入排序 

希尔排序的特性总结:

  • 希尔排序是对直接插入排序的优化
  • 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,就是直接插入排序。它与直接插入排序的区别就是多了一个预排序,这样使时间效率更好
  • 希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(N^1.3— N^2)
  • 稳定性:不稳定

3.选择排序算法

选择排序:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在未排序序列的起始位置,直到全部待排序的数据元素排完即可  

3.1 直接选择排序

直接选择排序: 每一次从待排序的数据元素中选出最小(或最大)的一个元素,与未排序序列的起始位置交换,直到全部待排序的数据元素排完即可

//直接选择排序
public void directSelectSort(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        int min = i;
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[min]) {
                min = j;
            }
        }
        int tmp = arr[i];
        arr[i] = arr[min];
        arr[min] = tmp;
    }
}

除了上面的每次使一个元素归位以外,还可以每次使两个元素归位,每次遍历找到最小的和未排序的第一个元素交换,找到最大的和未排序的最后一个元素交换

public void directSelectSort(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        int min = i;
        int max = i;
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[min]) {
                min = j;
            }
            if (arr[j] > arr[max]) {
                max = j;
            }
        }
        int tmp = arr[i];
        arr[i] = arr[min];
        arr[min] = tmp;
        if (max == i) {
            max = min;
        }
    }
}

 但是需要注意一个问题就是当待排序的数据元素中第一个是最大值时,应该怎么办?

当待排序的数据元素中第一个是最大值时,在将待排序中最小的值与待排序第一个值交换后,还需要将 max 等于 min

直接选择排序的特性总结:

  • 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  • 时间复杂度:O(N^2)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

3.2 堆排序 

在上一篇文章中我们已经学习了堆,大家也了解了什么是堆,也模拟实现了堆,那接下来我们就来看看如何采用堆来进行排序

如果想要进行堆排序首先需要构造一个堆,如果是升序就构大堆降序就构小堆

然后构造好堆后,让堆顶元素与最后一个有效元素交换位置,那么最后一个有效元素也就排好序了,将有效元素减一,在向下调整创建堆。然后依次这样交换向下调整知道所有位置的元素都排好序 

假设我们现在有一组数据,需要将其排升序 

//堆排序
public void heapSort(int[] arr) {
    buildHeap(arr);//建堆
    int size = arr.length - 1;
    int first = 0;
    while (first < size) {
        int tmp = arr[first];
        arr[first] = arr[size];
        arr[size] = tmp;
        size--;
        shiftDown(arr,first ,size+1);
    }
}
//建堆
public void buildHeap(int[] arr) {
    if (arr == null) {
        return;
    }
    int lastNoLeafNode = (arr.length - 1 - 1) / 2;
    while (lastNoLeafNode >= 0) {
        shiftDown(arr,lastNoLeafNode,arr.length);
        lastNoLeafNode--;
    }
}

//向下调整,建大顶堆
public void shiftDown(int[] arr, int parent, int size) {
    int child = parent * 2 + 1;
    while (child < size) {
        if (child + 1 < size && arr[child + 1] > arr[child]) {
            child += 1;
        }
        if (arr[parent] >= arr[child] ) {
            break;
        } else {
            int tmp = arr[parent];
            arr[parent] = arr[child];
            arr[child] = tmp;
            parent = child;
            child = parent * 2 + 1;
        }
    }
}

堆排序的特性总结:

  • 堆排序使用堆来选数,效率就高了很多。
  • 时间复杂度:O(N*logN)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

4.交换排序算法

交换排序基本思想:所谓交换排序,也就是将数据序列中的两个数据值进行比较,满足条件则进行交换,主要思想就是要进行交换

升序排序:就把数据值大的向后移动,数据值小的向前移动 

降序排序:就把数据值小的向后移动,数据值大的向前移动 

4.1 冒泡排序

冒泡排序的思想:每比较一趟就会使一个值进行归位,所以一共要比较 n-1 趟,才能使全部数据归位

假设现在有这样一组数据序列 {2,5,4,6,3,1},要通过冒泡排序算法来进行排序  

//冒泡排序
public void bubbleSort(int[] arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        for (int j = 1; j < arr.length - i; j++) {
            if (arr[j - 1] > arr[j]) {
                int tmp = arr[j - 1];
                arr[j - 1] = arr[j];
                arr[j] = tmp;
            }
        }
    }
}

如果我们数据序列本来就是有序的,难道我们还需要比较 n-1 趟嘛?

答:不需要,我们直接用一个变量来记录是否交换了数据,如果比较完一趟后并没有发生交换直接跳出即可

冒泡排序的特性总结:

  • 冒泡排序是一种非常容易理解的排序
  • 时间复杂度:O(N^2)
  • 空间复杂度:O(1)
  • 稳定性:稳定 

4.2 快速排序 

快速排序基本思想:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

public void quickSort(int[] arr,int left,int right) {
    if (left >= right) {
        return;
    }
    
    //调用快速排序
    int mid = hoareSort(arr,left,right);

    quickSort(arr,left,mid-1);

    quickSort(arr,mid + 1,right);

}

上述是采用递归的方法来调用快速排序算法,首先将一个大排序通过递归分成一些小排序来进行排序

那接下来我们就学习三种快速排序算法,分别是hoare法、挖坑法、左右指针法 

4.2.1 hoare 法 

基准值key通常指向第一个位置,然后从右边找比基准值小的值,左边找比基准值大的值,找到之后进行交换,当 left 和 right 指向同一个地方说明基准值的最终位置找到

public int hoareSort(int[] arr,int left,int right) {
    int key = left;
    while (left < right) {
        while (left < right && arr[right] >= arr[key]) {
            right--;
        }
        while (left < right && arr[left] <= arr[key]) {
            left++;
        }
        swap(arr,left,right);
    }
    swap(arr,key,left);
    return left;
}

public void swap(int[] arr,int left,int right) {
    int tmp = arr[left];
    arr[left] = arr[right];
    arr[right] = tmp;
}

4.2.2 挖坑法

首先将第一个值当作基准值放入 key 中,那么第一个位置就形成了一个坑,从右边找比基准值小的放入左边的坑中,那么右边的这个位置也就形成了一个坑,再从左边找比基准值大的放入右边的坑中,最后 left 和 right 同时指向的地方就是基准值最终的位置 

public int digPitSort(int[] arr,int left,int right) {
    int key = arr[left];
    while (left < right) {
        while (left < right && arr[right] >= key) {
            right--;
        }
        arr[left] = arr[right];
        while (left < right && arr[left] <= key) {
            left++;
        }
        arr[right] = arr[left];
    }
    arr[left] = key;
    return left;
}

4.2.3 前后指针法 

首先将第一个值当作基准值将其下标放入 key 中,然后让 after 存放第一个元素的下标,front 存放第二个元素的下标,front < right 就进入循环,然后判断 front 下标里面的元素是否小于基准值,如果小于再判断 ++after 是否不等于基准值,如果满足就进行交换,然后再 front++ 。直到 front 不小于 right 不进入循环,然后将基准值与 after 的位置交换,基准值归位。

//前后指针法
public int frontBack(int[] arr,int left,int right) {
    int key = left;
    int after = left;
    int front = left + 1;
    while (front <= right) {
        if (arr[front] < arr[key] && arr[++after] != arr[key]) {
            swap(arr,after,front);
        }
        front++;
    }
    swap(arr,key,after);
    return after;
}

public void swap(int[] arr,int after,int front) {
    int tmp = arr[after];
    arr[after] = arr[front];
    arr[front] = tmp;
}

4.2.4 三数取中法 

当序列为有序的时候,快速排序的时间复杂度为 O(N^2) 

为了避免这种情况也就可以采用三数取中法 

三数取中法思路:对比第一个下标的数和中间下标的数与最后下标的数,返回中间数(不是最大也不是最小)

//三数取中法
public int getMidIndex(int[] arr,int left,int right) {
    int mid = (left + right) >> 1;
    if (arr[left] < arr[mid])
    {
        if (arr[mid] < arr[right]) {
            return mid;
        } else if (arr[left] > arr[right]) {
            return left;
        } else {
            return right;
        }
    } else {
        if (arr[mid] > arr[right]) {
            return mid;
        } else if (arr[left] < arr[right]) {
            return left;
        } else {
            return right;
        }
    }
}

快速排序的特性总结:

  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(logN)
  4. 稳定性:不稳定 

5.归并排序算法 

归并排序基本思想: 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

//归并排序
public void mergeSort(int[] arr,int left,int right) {
    //分解
    if (left >= right) {
        return;
    }
    int mid = (left + right) / 2;
    mergeSort(arr,left,mid);
    mergeSort(arr,mid+1,right);

    //合并
    merge(arr,left,mid,right);
}
public void merge(int[] arr,int left,int mid,int right) {
    int s1 = left;
    int s2 = mid + 1;
    int[] ret = new int[(right - left) + 1];
    int i = 0;
    while (s1 <= mid && s2 <= right) {
        if (arr[s1] <= arr[s2]) {
            ret[i++] = arr[s1++];
        } else {
            ret[i++] = arr[s2++];
        }
    }
    while (s1 <= mid) {
        ret[i++] = arr[s1++];
    }
    while (s2 <= right) {
        ret[i++] = arr[s2++];
    }
    for (int j = 0; j < ret.length; j++) {
        arr[j+left] = ret[j];
    }
}

 归并排序的特性总结:

  1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(N)
  4. 稳定性:稳定

6. 排序算法复杂度及稳定性分析

排序方法最好情况平均情况最坏情况空间复杂度稳定性
冒泡排序O(n)O(n^2)O(n^2)O(1)稳定
插入排序O(n)O(n^2)O(n^2)O(1)稳定
选择排序O(n^2)O(n^2)O(n^2)O(1)不稳定
希尔排序O(n)O(n^1.3)O(n^2)O(1)不稳定
堆排序O(n * log(n))O(n * log(n))O(n * log(n))O(1)不稳定
快速排序O(n * log(n))O(n * log(n))O(n^2)O(log(n)) ~ O(n)不稳定
归并排序O(n * log(n))O(n * log(n))O(n * log(n))O(n)稳定

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

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

相关文章

前期软件项目评估偏差,如何有效处理?

1、重新评估制定延期计划 需要对项目进行重新评估&#xff0c;将新的评估方案提交项目干系人会议&#xff0c;开会协商一致后按照新的讨论结果制定计划&#xff0c;并实施执行。 软件项目评估偏差 怎么办&#xff1a;重新评估制定延期计划2、申请加资源 如果项目客户要求严格&a…

用股票交易量查询接口是怎么查询a股全天总成交量的?

用股票交易量查询接口是怎么查询a股全天总成交量的&#xff1f;今天下班就以通达信给大家讲解一下&#xff0c;通常是在K线图的底部状态栏&#xff0c;可以在日线进行查看a股成交量。在市场栏底部的子图中。 有当天成交的数量。成交量是表示一定的时间内已经成交的中的成交数量…

【数据挖掘】期末复习笔记(重点知识)

Data Mining 一、概述 1.1 数据挖掘 VS 机器学习 VS 深度学习 VS 知识发现 知识发现&#xff1a; 知识发现就是在数据中发掘知识&#xff0c;将低层次的原始数据转换为高层次的信息。 数据挖掘&#xff1a; 数据挖掘是用一系列的方法或算法从数据中挖掘有用的信息&#xf…

Android中的MVC、MVP、MVVM架构你清楚不?(附实现代码)

01 架构介绍 先来看一下MVC、MVP、MVVM的架构图。 从这些架构图中&#xff0c;可以看到每种架构都有3个模块以及数据流动方向箭头。 模块 在系统架构中&#xff0c;首先要做的就是把系统整体按照一定的原则划分成模块。 数据流动 模块划分之后&#xff0c;模块之间的通信&…

工程监测多通道振弦模拟信号采集仪VTN的MODBUS 通讯协议

工程监测多通道振弦模拟信号采集仪VTN的MODBUS 通讯协议 在 MODBUS 协议下&#xff0c;所有寄存器被定义为“保持寄存器” &#xff08;详见 MODBUS 通讯协议标准说明&#xff09;&#xff0c; 设备支持基于 MODBUS 协议的多个连续寄存器读取、单个寄存器写入两种指令码&#x…

电液伺服阀控制器YY-100

供电电源&#xff1a; 24V DC(18&#xff5e;36V)&#xff1b; 控制输入&#xff1a; -10V&#xff5e;10V DC&#xff1b;最大输出&#xff1a; 70mA &#xff1b;增益 &#xff1a; 调节范围——1&#xff5e;40 mA&#xff08;出厂设置——4 mA&#xff09;&#xff1b; 偏置…

C语言从0到1算法小白训练营——day2

我们学习不仅仅是要把难的学会&#xff0c;也要注重基础&#xff0c;注重内功。 接下来我们继续先从基础知识开始&#xff1a; 1. 字符串字符常量注释 1.1 字符串 如&#xff1a;“abc” ①定义&#xff1a;由双引号引起来的一串字符称为字符串。 ②C语言规定&#xff0c;…

【计算机网络】P1 - 物理层

物理层大纲物理层基本概念数据通信基础两种入网方式传输过程源系统、传输系统与目的系统数据与信号信源、信宿与信道三种通信方式两种传输方式大纲 物理层基本概念 物理层解决如何在传输媒体上&#xff08;同轴电缆&#xff0c;光纤等&#xff09;上传输数据比特流。主要任务为…

detach,主线程终止后子线程会结束吗

此前&#xff0c;我对detach的理解是&#xff0c;当主线程退出后&#xff0c;子线程能够继续存在。实际上&#xff0c;当主线程退出后&#xff0c;子线程也随之结束了。先看一个例子&#xff1a; #include <iostream> #include <thread> #include <unistd.h>…

交叉编译 zlib

交叉编译 zlib 概述 zlib 被设计为一个免费的、通用的、不受法律约束的、即不受任何专利保护的无损数据压缩库&#xff0c;可在几乎任何计算机硬件和操作系统上使用。zlib 数据格式本身可以跨平台移植。与Unix 压缩和 GIF 图像格式中使用的 LZW 压缩方法不同&#xff0c;zlib …

RocketMq使用规范(纯技术和实战建议)

概述&#xff1a; 使用规范主要从&#xff0c;生产、可靠性、和消费为轴线定义使用规范&#xff1b;kafka使用核心&#xff1a;削峰、解耦、向下游并行广播通知&#xff08;无可靠性保证&#xff09;和分布式事务&#xff0c;本规范仅从削峰、解耦、向下游并行广播通知论述&am…

OceanBase 4.0解读:兼顾高效与透明,我们对DDL的设计与思考

关于作者 谢振江&#xff0c;OceanBase 高级技术专家。 2015年加入 OceanBase, 从事存储引擎相关工作&#xff0c;目前在存储-索引与 DDL 组&#xff0c;负责索引&#xff0c;DDL 和 IO 资源调度相关工作。 回顾关系型数据库大规模应用以来的发展&#xff0c;从单机到分布式无…

什么是BOM?与焊盘不匹配,怎么办?

什么是BOM&#xff1f; 简单的理解就是&#xff1a;电子元器件的清单&#xff0c;一个产品由很多零部件组成&#xff0c;包括&#xff1a;电路板、电容、电阻、二三极管、晶振、电感、驱动芯片、单片机、电源芯片、升压降压芯片、LDO芯片、存储芯片、连接器座子、插针、排母、…

成为IT服务台经理需要什么技能

要给员工带来愉快的体验&#xff0c;就必须对你的服务台进行有效的管理。为此&#xff0c;了解为什么服务台经理的角色对于绘制企业组织良好的服务台至关重要。在本指南中&#xff0c;我们将深入探讨他们的角色、能力和贡献&#xff0c;以了解如何顺利处理服务台操作。 IT 服务…

【面试题】前端 移动端自适应?

移动端 h5 开发中有一个绕不开的话题&#xff1a;移动端自适应方案。移动端的设备尺寸不尽相同&#xff0c;要把 UI 设计图较好地展示在移动端上&#xff0c;需要让 h5 页面能自适应设备尺寸。接下来将对移动端自适应的相关概念、方案和其他一些常见问题做个介绍。概念简介大厂…

什么是 Web3?解读未来的去中心化网络:The Decentralized Internet of the Future Explained

目录 互联网的演化 什么是 Web 1.0? 什么是 Web 2.0? Web 2.0 变现与安全性 什么是 Web 3.0? 原生支付 创立公司的新方式 Web3 中的身份 如果你读到这篇文章,那么你已经是当代互联网世界的一员了。我们现在使用的网络和10年前大不相同。所以,互联网是怎么演化的,…

Centos7 安装 Mysql 8.0.32,详细完整教程(好文章!!)

mysql5.7的安装方式参考之前的文章&#xff1a; centos7 安装 Mysql 5.7.27&#xff0c;详细完整教程&#xff08;好文章&#xff01;&#xff01;&#xff09;_HD243608836的博客-CSDN博客 一、检查mysql版本冲突 先检查是否已经存在mysql&#xff0c;若存在卸载&#xff0…

大数据第一轮复习笔记(2)

Spark ./spark-submit --class com.kgc.myspark01.WordCount --master yarn --deploy-mode cluster /opt/myspark01-1.0-SNAPSHOT.jar 1.Client向YARN的ResourceManager申请启动Application Master。Client中创建SparkContext同时初始化中将创建DAGScheduler和TASKScheduler…

固态继电器的五大优势

固态继电器的优点和五个关键优势&#xff0c;现代电气控制系统因二极管、晶体管和晶闸管等固态器件的发明而得到极大的增强。对于加热器和电机等大负载设备&#xff0c;固态继电器可能比传统的机械继电器具有巨大的优势。 虽然并非适用于所有情况&#xff0c;但它们具有许多吸引…

前端——周总结系列五

JS的Map对象 概述 ES6新增的一种数据结构Map&#xff0c;对操作键值对很友好&#xff0c;键值对集合&#xff0c;提供属性和方法供开发者使用。存有键值对&#xff0c;键可以是任何数据类型&#xff1b;按照原始插入顺序存储&#xff08;FIFO&#xff09;原则&#xff1b;具有…