数据结构——常见的十种排序算法

news2024/11/17 13:20:45

一、常见的十种排序算法:
冒泡排序、选择排序、插入排序、归并排序、快速排序、希尔排序、堆排序、计数排序、桶排序、基数排序
1.【知识框架】
在这里插入图片描述

补充:
内部排序:整个排序过程完全在内存中进行。
外部排序:由于待排序记录数据量太大,内存无法容纳全部数据,需要借助外部存储。

二、排序方法

插入排序
•直接插入排序
1.算法思想
从待排序的第二个元素开始,向下扫描列表,比较这个目标值target与arr[i-1]、arr[i-2]的大小,依次类推。如果target的值小于或等于每一个元素值,那么每个元素都会向右滑动一个位置,一旦确定了正确位置j,目标值target(即原始的arr[i])就会被复制到这个位置。(例如:整理桥牌时,我们会将每一张牌插入到一个已经有序的序列的适当位置。为了给要插入的元素腾出空间,需要我们将已经有序的序列元素都向右移动一位)
2.算法实现

在这里插入图片描述

3.插入排序伪代码

 1 void InsertSort (ElemType A[], int n)
 2 {
 3    int i,j;
 4    for(i=2;i<=n;i++)
 5         if(A[i].key<A[i-1].key)
 6     {
 7         A[0]=A[i];
 8         for(j=i-1;A[0].key<A[j].key;--j)
 9                 A[j+i]=A[j];
10         A[j+i]=A[0];
11     }
12 }

4.稳定性

由于每次插入元素时总是从后往前先比较在移动,所以不会出现相同元素相对位置,发生变化的情况即直接插入排序是一个稳定的排序方法

5.时间复杂度:O(n²)

•希尔排序(缩小增量排序)

1.算法提出:
为解决插入排序每次只能将数据移动一位,在数组较大且基本无序的情况下性能出现的恶化所以引入其算法。
2.算法思想:
先取一个小于n的步长d1把表中全部记录分成d1个组,所有距离为d1的倍数的记录放在同一个组中,在各组中进行直接插入排序:然后取第二个步长d2<d1,重复上 述过程,直到所取到的d1=1,即所有记录已放在同一组中,再进行直接插入排序,由于此时已经具有较好的局部有序性,故可以很快得到最终结果。到目前为 止,尚未求得一个最好的增量序列,希尔提出的方法是d1=n/2,di+1=⌈di/2⌉ , 并且最后一个增量等于 1。
3.算法实现:
在这里插入图片描述

补充:操作原理时间复杂度与选取的增量序列有关且所取增量序列的函数介于O(N*logN)和O(n²)之间增量序列有很多种取法,但是使增量序列中的值没有除1之外的公因子,并且增量序列中最后一个值必须为1。

4.希尔排序伪代码

 1 void ShellSort (ElemType A[],int n){
 2 //对顺序表作希尔插入排序,基本算法和直接插入排序相比,做了以下修改:
 3 //1.前后记录位置的增量是dk,不是1
 4 //2.r[0]只是暂时存储单元,不是哨兵,当j<=0时,插入位置已到
 5       for(dk=n/2;dk>=1,dk=dk/2)                                   //步长变化
 6            for(i=dk+1;i<=n;++i) 
 7                  if(A[i].key<A[i-dk].key){                        //需将A[i]插入有序增量子表
 8                      A[0]=A[i];                                                       
 9                      for(j=i-dk;j>0&&A[0].key<A[j].key;j-=dk) 
10                           A[j+dk]=A[j];                           //记录后移,查找插入位置
11                      A[j+dk]=A[0];                                //插入
12                }//if
13   }

5.稳定性

当相同关键字的记录被划分到不同的子表时,可能会改变它们之间的相对次序,因此,希尔排序是一种不稳定的排序方法。例如,表L=[3,2,2].经过一趟排序后,L=[2,2,3],最终排序序列也是L=[2,2,3],显然2与2的相对次序已经发生了变化。

6.时间复杂度:O(N*logN)

选择排序

•简单选择排序

1.算法思想

Step1:将待排序数组分为有序和无序两组(初始情况下有序组为空)。

Step2:从左向右扫描无序组,找出最小的元素,将其放置在无序组的第一个位置。至此有序组++,无序组–;

Step3:重复Step2,直至无序组只剩下一个元素。

2.算法实现

3.选择排序伪代码

 1 void ShellSort (ElemType A[],int n){
 2 //对表A作简单的选择排序,A[]从0开始放元素
 3       for(i=0;i<=n-1;i++){                                //一共进行n-1趟
 4           min=i;                                          //记录最小元素位置
 5               for(j=i+1;j<n;j++)                          //在A[i...n-1]中选择最小的元素                                    
 6                      if(A[j]<A[min])
 7                            min=j;                         //更新最小元素位置
 8 if(min!=i)  swap(A[i],A[min]);                            //在第n个元素交换
 9                         
10                }
11 }

4.稳定性

选择排序的时间复杂度为O(n²),由于每次选择仅考虑某一位置上的数据情况,可能会破坏之前数据的相对位置,因此它是一种不稳定的排序方法。 例如:序列 [9,9,1]第一次就将第一个[9]与[1]交换,导致第一个9挪动到第二个9后面。

5.时间复杂度: O(n²)

补充:简单选择排序的比较次数与序列的初始排序无关。假设待排序的序列有 n个元素,选择排序的赋值操作介于0和3(n - 1次之间; 则比较次数永远都是n(n-1)/2; 而移动次数(即:交换操作)与序列的初始排序有关,介于 0 和 (n - 1) 次之间。当序列正序时,移动次数最少,为 0。当序列反序时,移动次数最多,为n - 1 次;逆序交换n/2次。选择排序的移动次数比冒泡排序少多了,由于交换所需CPU时间比 比较所需的CPU时间多,n值较小时,选择排序比冒泡排序快。

•堆排序

1.引入概念
堆是一棵顺序存储的完全二叉树。其中每个结点的关键字都不大于其孩子结点的关键字,这样的堆称为小根堆。
其中每个结点的关键字都不小于其孩子结点的关键字,这样的堆称为大根堆。
举例来说,对于n个元素的序列{R0, R1, … , Rn}当且仅当满足下列关系之一时,称之为堆:

2.算法思想
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
如图:

3.算法实现(大顶堆)
注:若n为数组的个数,那么就从2/n开始,依次循环。
4.堆排序伪代码
下面是建立大根堆的算法:

 1    void BuildMaxHeap (ElemType A[], int len) {
 2           for(int i=len/2;i>0;i--)  //从i=[n/2]~1,反复调整堆
 3               AdjustDown(A, i, len) ;
 4 
 5    }
 6     void AdjustDown (ElemType A[], int k, int len) {
 7                  //函数AdjustDown将元素k向下进行调整
 8             A[0]=A[k] ;                         //A[O]暂存
 9             for (i=2*k;i<=len;i*=2){      //沿 key较大的子结点向下筛选
10                   if (i<len&&A[i] <A[i+1])
11                         i++;
12                   if(A[0]>=A[i]) break;       // 筛选结束
13                   else{
14                         A[k]=A[i];                //将A[i]调整到双亲结点上
15                         k=i ;                        //修改k值,以便继续向下筛选
16             }
17     }//for
18     A[k]=A[0];                                   //被筛选结点的值放入最终位置
19    }

下面是堆排序算法:

1                   void HeapSort (ElemType A[],int len) {
2                        BuildMaxHeap (A,len) ;          //初始建堆
3                        for(i=len;i>1;i--){              //n-1 趟的交换和建堆过程
4                        Swap (A[i],A[1]) ;                / /输出堆顶元素(和堆底元素交换)
5                        AdjustDown (A, 1, i-1) ;}     //整理,把剩余的i-1个元素整理成堆
6                 }//for
7            }

下面是向上调整堆的算法:

 1                void AdjustUp (ElemType A[], int k) {
 2                 //参数k为向上调整的结点,也为堆的元素个数
 3                    A[0]=A[k] ;
 4                    int i=k/2;  //若结点值大于双亲结点, 则将双亲结点向下调,并继续向上比较 
 5                    while (i>0&&A[i]<A[0]) {           //循环跳出条件
 6                           A[k]=A[i];                    //双亲结点下调 
 7                           k=i;
 8                           i=k/2;                           //继续向上比较
 9             }//while
10             A[k]=A[0] ;                                  //复制到最终位置
11        }

5.稳定性
堆排序是一种不稳定的排序方法。因为在堆的调整过程中,关键字进行比较和交换所走的是该结点到叶子结点的一条路径,因此对于相同的关键字就可能出现排在后面的关键字被交换到前面来的。
6.时间复杂度:O(N*logN)。

交换排序

•冒泡排序

1.算法思想

冒泡排序是一种交换排序,它的实现原理是:两两比较相邻的记录值,如果反序则交换,直到没有反序的记录为准。对数组中的各数据,依次比较相邻的两元素的大小。如果前面的数据大于后面的数据,就交换这两个数据。经过第一轮的多次比较排序后,变可把最小的数据排好。再用同样的方法把剩下的数据逐个进行比较,最后便可按照从小到大的顺序排好数组各数据的顺序。

2.算法实现

  1. 冒泡排序伪代码
 1 void BubbleSort (ElemType A[],int n){
 2 //用冒泡排序将序列A中的元素按从小到大排列
 3          for(i=0;i<n-1;i++){
 4               flag=false;                           //标示本趟冒泡是否发生交换标志
 5               for(j=n-1;j>i;j--)               //一趟冒泡过程
 6                    if(A[j-i].key>A[j].key){     //若为逆序
 7                          swap(A[j-1],A[j]);       //交换
 8                          flag=true;
 9                    }
10              if(flag==false)
11                   return;                            //本趟遍历没有发生交换,说明已经有序
12          }
13 }

4.稳定性

冒泡排序是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。相同元素的前后顺序并没有改变,冒泡排序是一种稳定排序算法。

5.时间复杂度: O(n²)

补充:若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数C和记录移动次数M均达到最小值:Cmin = N - 1, Mmin = 0。所以,冒泡排序最好时间复杂度为O(N)。若初始文件是反序的,需要进行 N -1 趟排序。每趟排序要进行 N - i 次关键字的比较(1 ≤ i ≤ N - 1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:Cmax = N(N-1)/2 = O(N^2) Mmax = 3N(N-1)/2 = O(N2)冒泡排序的最坏时间复杂度为O(N2)。因此,冒泡排序的平均时间复杂度为O(N^2)。当数据越接近正序时,冒泡排序性能越好。

•快速排序

1.基本概念

快速排序为应用最多的排序算法,因为快速二字而闻名。快速排序和归并排序一样,采用的都是分治思想。快速排序可以分为:单路快速排序,双路快速排序,三路快速排序,他们区别在于选取几个指针来对数组进行遍历 。

1.算法思想

快速排序是对冒泡排序的一种改进。其基本思想是基于分治法的:在待排序表L[`1…n]中任取一个元素 pivot作为基准,通过一趟排序表将待排序表划分为独立的两部分
L[1…k-1]和L[k+1…n],使得L[1…k-1]中所有元素小子pivot, L[k+1…n]中所有元素大于或等于pivot,则pivot放在了其最终位置L(k)上,这个过程称作一趟快速排序。 而后分别递归地对两个子表重复上述过程,直至每部分内只有一个元素或空为止,即所有元素放在了其最终位置上。

2.算法实现

注:后找小前找大

  1. 快速排序伪代码

首先假设划分算法已知,记为Partition(),返回的是上述中的k,注意到L(k)已经在最终的位置,所以可以先对表进行划分,而后对两个表调用同样的排序操作。因此可以递归地调用快速排序算法进行排序,具体的程序结构如下:

1 void QuickSort (ElemType A[],int low,int high){
2
3 if (low<high) { //递归跳出的条件
4 //Partition()就是划分操作,将表low-high划分为满足上述条件的两个子表
5 int pivotpos-=Partition(A, low,high); //划分//依次对两个子表进行递归排序
6 QuickSort MB (A, low,pivotpos-1);
7 QuickSort (A, pivotpos+1,high) ;
8 }
9 }

从上面的代码也 不难看出快速排序算法的关键在于划分操作,同时快速排序算法的性能也主据要取决于划分操作的好坏。假设每次总是以当前表中第一个元素作为枢轴值 (基准)对表进行划分,则必须将表中比枢轴值大的元素向右移动,比枢轴值小的元素向左移动,使得一趟Partition()操作之后,表中的元素被枢轴值一分为二。
4.稳定性
快速排序有两个方向,左边的i下标一直往右走,当a[i] <= a[center_index],其中center_index是中枢元素的数f组下标,一般取为数组第0个元素。而右边的j下标一直往左走,当a[j] > a[center_index]。如果i和j都走不动了,i <= j, 交换a[i]和a[j],重复上面的过程,直到i>j。 交换a[j]和a[center_index],完成一趟快速排序。在中枢元素和a[j]交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为 5 3 3 4 3 8 9 10 11, 现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定的排序算法,不稳定发生在中枢元素和a[j] 交换的时刻。
5.时间平均复杂度: O(nlog2n)。

基数排序(桶排序)
1.基本概念
基数排序(Radix Sort)属于分配式排序,又称"桶子法"(Bucket Sort或Bin Sort),将要排序的元素分配到某些"桶"中,以达到排序的作用。基数排序属于稳定的排序,其时间复杂度为nlog®m (其中r为的采取的基数,m为堆数),基数排序的效率有时候高于其它比较性排序。

2.算法思想

  1. 根据输入建立适当个数的桶,每个桶可以存放某个范围内的元素;
    2.将落在特定范围内的所有元素放入对应的桶中;
    3.对每个非空的桶中元素进行排序,可以选择通用的排序方法,比如插入、快排;
    4.按照划分的范围顺序,将桶中的元素依次取出。排序完成。
  2. 算法实现
    数组: int arr[] = {278,109,63,930,589,184,505,269,8,83};

个位分配,个位收集,先进后出。

十位分配,十位收集,先进后出。

百位分配,百位收集,先进后出。

4.稳定性
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优 先级排序,最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以其是稳定的排序算法。

5.时间平均复杂度:O(d(n+r))

归并排序(二路归并排序)

1.基本概念
归并排序是通过“归并”操作完成排序的,将两个或者多个有序子表归并成一个子表。归并排序是“分治法”的一个非常典型的应用,同事它也是递归算法的一个好的实例。它将问题分成一些小的问题然后递归求解,而治就是把分阶段的答案拼起来。
2.算法思想
将一个大小为 n 的数组排序。归并排序算法的排序步骤是:

1.将所有的数字放入一个无序的堆。
2.将堆分成两部分,现在你有两个无序的堆。
3.持续将无序的堆拆分,直到无法再拆分为止,你将得到 n 个堆,每一个堆中有一个数字。
4.现在开始将这些堆按照一定顺序按对合并。每一次合并过程中,将堆中的数字放入有序的队列。这一点很容易实现,因为每一个独立的堆中的内容都是有序的。
3.算法实现

数组: int arr[] = {3,6,1,7,9,4,5,8,2}
二路归并排序的过程如图所示:

4.稳定性
归并排序的空间复杂度O(n)。另外,归并排序中归并的算法并不会将相同关键字的元素改变相对次序,所以归并排序是稳定的。
5.时间平均复杂度:O(nlog2n)。

后续未完…

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

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

相关文章

python进行接口自动化测试

一、接口自动化测试的流程 1、需求分析 1、1请求&#xff08;url,方法、数据&#xff09; 2、挑选需要做自动化测试的接口 3、设计自动化测试用例 4、搭建自动化测试环境 5、设计自动化执行框架&#xff08;报告、参数化、 用例执行框架&#xff09; 6、编写代码 7、执…

工程派工单,建筑工程派工单

工程派工单是指建设项目管理人员或工程维修人员发出的文件&#xff0c;用于标明工人或维修人员在建设项目或设备中处理或维修问题的任务。派工单包括建设项目的实际维护任务、所需材料、工具等信息&#xff0c;以及具体的执行人员和完成时间。工程派工单是保证建设项目顺利开展…

用《斗破苍穹》的视角打开C#3 标签与反射(人物创建与斗技使用)

随着剧情的发展&#xff0c;主线人物登场得越来越多&#xff0c;时不时跳出一个大佬&#xff0c;对我张牙舞爪地攻击。眼花缭乱的斗技让我不厌其烦&#xff0c;一个不小心&#xff0c;我就记不清楚在哪里遇上过什么人&#xff0c;他会什么斗技了。这时候&#xff0c;我就特别希…

Centos中清除因程序异常终止,导致的残留的Cache/buff_drop_caches命令---linux工作笔记063

我这里因为nifi程序背压设置的不合理,导致,内存和CPU消耗过高,系统崩溃,但是重启NIFI以后,发现 对应的执行top命令,看到,系统的buff/cache 依然没有减少,说明内存被浪费了,残留在这里没有被回收. 用这个办法执行这个命令; linux会自动触发清理,但是只有在内存不够用的时候才会…

计算机竞赛 题目:基于机器视觉opencv的手势检测 手势识别 算法 - 深度学习 卷积神经网络 opencv python

文章目录 1 简介2 传统机器视觉的手势检测2.1 轮廓检测法2.2 算法结果2.3 整体代码实现2.3.1 算法流程 3 深度学习方法做手势识别3.1 经典的卷积神经网络3.2 YOLO系列3.3 SSD3.4 实现步骤3.4.1 数据集3.4.2 图像预处理3.4.3 构建卷积神经网络结构3.4.4 实验训练过程及结果 3.5 …

沈阳陪诊系统|沈阳陪诊系统开发|沈阳陪诊系统功能和优势

在现代医疗服务中&#xff0c;陪诊系统服务正变得越来越重要。这项创新的服务提供了一种全新的方式&#xff0c;帮助患者在医院就诊时获得更好的照顾和支持。无论是面对复杂的医学流程还是需要心理支持&#xff0c;陪诊系统服务都能够为患者提供方便、专业的帮助。陪诊系统服务…

自学SLAM(3)---保姆教程教你如何使用摄像头运行ORB-SLAM2

前言 上一篇文章我讲述了如何使用自己的视频运行ORB-SLAM2 链接如下&#xff1a; 链接: 上一篇&#xff0c;环境搭建及使用自己的视频运行ORB-SLAM2 没有搭建环境的朋友看上面我的链接哦&#xff0c;里面有超详细的环境搭建&#xff0c;一步一步来保姆级别的哦 那么本篇&#…

【mysql 大表清理】磁盘占用太多,清理无效大表

在使用MySQL数据库时&#xff0c;有时候由于数据量增加或者磁盘空间限制&#xff0c;会导致数据库磁盘空间不足的问题。这会影响到数据库的正常运行&#xff0c;需要及时清理磁盘空间来解决问题。本文将介绍如何清理MySQL数据库的磁盘空间&#xff0c;并给出示例以帮助读者更好…

wget出现无法建立SSL连接的问题

出现这个问题的原因&#xff0c;这是因为wget在使用https协议的时候&#xff0c;默认会去验证网站的证书&#xff0c;而这个证书验证经常会失败&#xff0c;加上"--no-check-certificate"选项&#xff0c;就能排除掉这个错误

MongoEngine 简介安装、连接、数据类型及其参数详解

文章目录 前言一、MongoEngine 简介二、MongoEngine的安装与连接1. 安装MongoEngine2. 连接到MongoDB3. 定义数据模型 三、MongoEngine模型介绍1. 常见数据类型2. 数据类型参数 总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&#xff0c;方…

除静电离子风棒的工作原理及应用

除静电离子风棒是一种常见的除静电设备&#xff0c;它的工作原理是通过产生大量的负离子来中和物体表面的静电电荷&#xff0c;从而达到除静电的目的。 静电离子风棒内部装有一个电离器&#xff0c;电离器会将空气中的氧气分子或水分子电离成正、负离子。这些带电的离子在空气…

ubuntu疑难杂症

1.ubuntu 使用apt 安装软件时提示出现不能获得lock $ sudo rm /var/lib/dpkg/lock$ sudo dpkg --configure -a$ sudo rm /var/lib/apt/lists/lock

什么是防抖和节流

防抖和节流都是前端开发中常用的优化性能的技术。 一、定义 防抖&#xff1a; 防抖指的是在事件触发后&#xff0c;在规定的时间内若再次触发&#xff0c;则重新计时&#xff0c;直到规定时间内没有再次触发事件&#xff0c;才执行事件处理。这样可以避免在短时间内频繁地触发…

vtk之【vtkPolyData、vtkCell、vtkPoints】

文章目录 一,vtkPolyData、cell、point1) 例子2) vtkPolyData、vtkCell、vtkPoints 二,vtkNew<>与vtkSmartPointer<>的区别:三&#xff0c;补充 一,vtkPolyData、cell、point 1) 例子 /*** vtkNew 是一个类模板* vtkNew<> 是一个简单的 RAII&#xff08;Res…

【ARM CoreLink 系列 5 -- CI-700 控制器介绍 】

文章目录 1.1 什么是 CI-700?1.1.1 关于 CI-7001.1.2 CI-700 特点1.2 全局配置参数1.2.1 寻址能力1.3 组件和配置1.3.1 CI-700 互联的结构1.3.2 Crosspoint(XP)1.3.3 外部接口1.4 组件(Components)1.1 什么是 CI-700? CI-700是一种AMBA 5 CHI互连,具有可定制的网状拓扑结构…

onlyOfice取消上传文件大小的限制

进入docker容器 docker exec -it 容器名ID bash 编辑配置文件 #如果不能编辑,需安装vim apt-get update apt-get install vim #如果不能安装vim&#xff0c;可以在容器外部编辑配置文件后上传至容器&#xff1a; docker cp /home/file/文件 容器id:/etc/onlyoffice/d…

2023/10/7 -- ARM

【程序状态寄存器读写指令】 1.指令码以及格式 mrs:读取CPSR寄存器的值 mrs 目标寄存器 CPSR&#xff1a;读取CPSR的数值保存到目标寄存器中msr:修改CPSR寄存器的数值msr CPSR,第一操作数:将第一操作数的数值保存到CPSR寄存器中//修改CPSR寄存器&#xff0c;也就表示程序的状…

频敏变阻器 BP4G-31511/06350 BP4G-25010/04563 结构简单、操作方便

BP4G系列频敏变阻器&#xff08;以下简称BP4G&#xff09;适用于14-1000KW绕线型异步感应电机作重载偶尔起动用。它具有结构简单、操作方便、维护容易等优点&#xff0c;是一种理想的电动机起动装置 BP4G系列频敏变阻器(以下简称BP4G)适用于14-1000KW绕线型异步感应电机作重载…

川西旅游网系统-前后端分离(前台vue 后台element UI,后端servlet)

前台&#xff1a;tour_forword: 川西旅游网前端----前台 (gitee.com) 后台&#xff1a;tour_back: 川西旅游网-------后台 (gitee.com) 后端 &#xff1a;tour: 川西旅游网------后端 (gitee.com)

点餐小程序实战教程05-用户注册

上一篇我们讲解了用户身份识别的问题&#xff0c;本篇我们讲解一下注册功能以及审核功能。 1 表单容器 注册功能是使用表单容器完成&#xff0c;表单容器会根据数据源的字段来自动的生成页面&#xff0c;从右侧的组件区拖入表单容器 表单容器需要选择我们创建的用户管理的数…