数据结构之七大排序

news/2024/5/6 18:30:12/文章来源:https://blog.csdn.net/T2697142573/article/details/129940893

在这里插入图片描述

数据结构之七大排序

  • 🔆排序的概念及其运用
    • 排序的概念
    • 常见的排序算法
  • 🔆插入排序
    • 直接插入排序
    • 希尔排序
  • 🔆选择排序
    • 直接选择排序
    • 堆排序
  • 🔆交换排序
    • 冒泡排序
    • 快排
  • 🔆归并排序
  • 🔆非比较排序
  • 🔆结语

在这里插入图片描述

🔆排序的概念及其运用

排序的概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

常见的排序算法

在这里插入图片描述

🔆插入排序

基本思想:
直接插入排序是一种简单的插入排序法,其基本思想是:
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列
实际中我们玩扑克牌时,就用了插入排序的思想
在这里插入图片描述

直接插入排序

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

代码实现:

//直接插入排序
void InsertSort(int* arr, int n) {assert(arr);int end;int tmp;for (int i = 1; i < n; ++i) {end = i - 1;tmp = arr[i];while (end >= 0) {if (tmp < arr[end]) {arr[end + 1] = arr[end];end--;}else {break;}}arr[end + 1] = tmp;}}

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

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

希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序
在这里插入图片描述

代码实现:

//希尔排序
void ShellSort(int* arr, int n) {assert(arr);int gap = n;while (gap > 1) {gap /= 2;int end;int tmp;for (int i = gap; i < n; ++i) {end = i - gap;tmp = arr[end + gap];while (end >= 0) {if (tmp < arr[end]) {arr[end + gap] = arr[end];end -= gap;}else {break;}}arr[end + gap] = tmp;}}}

希尔排序的特性总结:

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定:
    《数据结构(C语言版)》— 严蔚敏
    在这里插入图片描述
    《数据结构-用面相对象方法与C++描述》— 殷人昆
    在这里插入图片描述
    因为咋们的gap是按照Knuth提出的方式取值的,而且Knuth进行了大量的试验统计,我们暂时就按照:O(n1.25)O(n^{1.25})O(n1.25)O(1.6∗n1.25)O(1.6*n^{1.25})O(1.6n1.25)来算。
  4. 稳定性:不稳定

🔆选择排序

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

直接选择排序

  • 在元素集合array[i]–array[n-1]中选择关键码最大(小)的数据元素
  • 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
  • 在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,直到集合剩余1个元素
    在这里插入图片描述
//交换数据
void Swap(int* e1, int* e2) {int tmp = *e1;*e1 = *e2;*e2 = tmp;
}//直接选择排序
void SelectSort(int* arr, int n) {assert(arr);//一次循环同时找最大值下标和最小值下标for (int i = 0; i < n / 2; ++i) {int min = i;int max = i;for (int j = i + 1; j < n - i; ++j) {if (arr[j] < arr[min]) {min = j;}else if (arr[j] > arr[max]) {max = j;}}Swap(arr + i, arr + min);//最大值是最左端,第一次交换数据,就把最左端的数据交换为最小值,则此时最大值在原本最小值的位置if (max == i) {max = min;}Swap(arr + n - i - 1, arr + max);}}

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

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

堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。
在这里插入图片描述

代码实现:

//交换元素
void Swap(int* e1, int* e2) {int tmp = *e1;*e1 = *e2;*e2 = tmp;
}//向下调整算法   大堆
void AdjustDown(int* arr, int n, int parent) {assert(arr);int child = parent * 2 + 1;while (child < n) {if ((child + 1) < n && arr[child] < arr[child + 1]) {child++;}if (arr[parent] < arr[child]) {Swap(arr + parent, arr + child);}parent = child;child = parent * 2 + 1;}
}//堆排序
void HeapSort(int* arr, int n) {assert(arr);//建大堆for (int i = (n / 2) - 1; i >= 0; --i) {AdjustDown(arr, n, i);}//替换堆顶for (int i = n - 1; i > 0; --i) {Swap(arr, arr + i);AdjustDown(arr, i, 0);}
}

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

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

🔆交换排序

基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

冒泡排序

在这里插入图片描述
代码实现:

//交换元素
void Swap(int* e1, int* e2) {int tmp = *e1;*e1 = *e2;*e2 = tmp;
}//冒泡排序
void BubbleSort(int* arr, int n) {assert(arr);for (int i = 0; i < n - 1; ++i) {bool flag = true;   //如果本趟冒泡里面没有数据交换,就证明数据已经有序,flag就是true,退出排序,for (int j = 0; j < n - 1 - i; ++j) {if (arr[j] > arr[j + 1]) {Swap(arr + j, arr + j + 1);flag = false;   //如果本趟冒泡里面有交换数据,就证明数据还是无序}}if (flag) {break;}}
}

冒泡排序的特性总结:

  1. 冒泡排序是一种非常容易理解的排序
  2. 时间复杂度:O(N^2) - 最坏O(N ^ 2) 最好O(N)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定

快排

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

将区间按照基准值划分为左右两半部分的常见方式有:
1.hoare版本
在这里插入图片描述
2.挖坑法
在这里插入图片描述
3.前后指针法
在这里插入图片描述

快速排序优化

  1. 三数取中法选key
  2. 递归到小的子区间时,可以考虑使用插入排序
  3. 三路划分,解决大部分数相等的情况

代码实现:

//交换元素
void Swap(int* e1, int* e2) {int tmp = *e1;*e1 = *e2;*e2 = tmp;
}//快速排序
// 将大于key的放左边,小于key的放右边// 三数取中找keyi
int GetKeyi(int* arr, int left, int right) {assert(arr);if (right - left < 2) {return left;}int mid = (right + left) / 2;if (arr[left] > arr[right]) {if (arr[left] < arr[mid]) {return left;}else {if (arr[mid] > arr[right]) {return mid;}else {return right;}}}else {if (arr[right] < arr[mid]) {return right;}else {if (arr[mid] > arr[left]) {return mid;}else {return left;}}}}//hoare版本
int partion1(int* arr, int left, int right) {assert(arr);right--;int keyi = GetKeyi(arr, left, right);Swap(arr + keyi, arr + left);keyi = left;while (left < right) {//右边先走找小while (arr[right] > arr[keyi]) {right--;}//左边找大while (right > left && arr[left] <= arr[keyi]) {left++;}if (right > left) {Swap(arr + left, arr + right);}}Swap(arr + keyi, arr + left);return left;
}//挖坑法
int partion2(int* arr, int left, int right) {assert(arr);right--;int midi = GetKeyi(arr, left, right);Swap(arr + midi, arr + left);int hole = left;  //坑位置int key = arr[left];while (left < right) {while (left < right && arr[right] > key) {right--;}arr[hole] = arr[right];hole = right;while (left < right && arr[left] <= key) {left++;}arr[hole] = arr[left];hole = left;}arr[hole] = key;return hole;
}//前后指针版本
int partion3(int* arr, int left, int right) {assert(arr);int midi = GetKeyi(arr, left, right-1);Swap(arr + left, arr + midi);int prev = left;int cur = prev + 1;while (cur < right) {if (arr[cur] <= arr[left]) {++prev;//prev和cur位置相同就不用交换if (prev != cur) {Swap(arr + prev, arr + cur);}}++cur;}Swap(arr + prev, arr + left);return prev;
}void QuickSort(int* arr, int left, int right) {assert(arr);if (right - left <= 7) {if (right - left > 1) {InsertSort(arr + left, right - left);}return;}/*if (right - left <= 10) {if (right - left > 1) {InsertSort(arr + left, right - left + 1);}return;}*/int diy = partion2(arr, left, right);//左半边QuickSort(arr, left, diy);//右半边QuickSort(arr, diy + 1, right);
}

非递归代码实现:
注意:这里的ST是博主自定义的栈结构,栈数据结构还需自己实现

//快排-非递归的实现
void QuickSort2(int* arr, int left, int right) {assert(arr);ST* st = STCreate();STPush(st, left);STPush(st, right);while (!STEmpty(st)) {right = STTop(st);STPop(st);left = STTop(st);STPop(st);if (right - left <= 7) {if (right - left > 1) {InsertSort(arr + left, right - left);}continue;}int diy = partion1(arr, left, right);//以基准值为分割点,形成左右两部分:[left, div) 和 [div+1, right)STPush(st, left);STPush(st, diy);STPush(st, diy + 1);STPush(st, right);}STDestroy(st);
}

快速排序的特性总结:

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

🔆归并排序

基本思想:
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并排序核心步骤:
在这里插入图片描述
在这里插入图片描述

代码实现-递归

//归并排序
void PartMerge(int* arr, int left, int right, int mid, int* tmp) {int cur1 = left, cur2 = mid + 1;int index = left;while (cur1 <= mid && cur2 <= right) {if (arr[cur1] < arr[cur2]) {tmp[index++] = arr[cur1++];}else {tmp[index++] = arr[cur2++];}}while (cur1 <= mid) {tmp[index++] = arr[cur1++];}while (cur2 <= right) {tmp[index++] = arr[cur2++];}for (int i = left; i < index; ++i) {arr[i] = tmp[i];}
}void _MergeSort(int* arr, int left, int right, int* tmp) {assert(arr && tmp);if (left >= right) {return;}int mid = (left + right) >> 1;// [left, mid] [mid+1, right]_MergeSort(arr, left, mid, tmp);_MergeSort(arr, mid + 1, right, tmp);PartMerge(arr, left, right, mid, tmp);
}void MergeSort(int* arr, int n) {assert(arr);int* tmp = (int*)malloc(n * sizeof(int));assert(tmp);_MergeSort(arr, 0, n - 1, tmp);free(tmp);
}

非递归

//归并非递归
void PartMerge(int* arr, int left, int right, int mid, int* tmp) {int cur1 = left, cur2 = mid + 1;int index = left;while (cur1 <= mid && cur2 <= right) {if (arr[cur1] < arr[cur2]) {tmp[index++] = arr[cur1++];}else {tmp[index++] = arr[cur2++];}}while (cur1 <= mid) {tmp[index++] = arr[cur1++];}while (cur2 <= right) {tmp[index++] = arr[cur2++];}for (int i = left; i < index; ++i) {arr[i] = tmp[i];}
}void MergeSort2(int* arr, int n) {assert(arr);int* tmp = (int*)malloc(n * sizeof(int));assert(tmp);int gap = 1;while (n / gap >= 1) {for (int i = 0; i < n; i += gap * 2) {//[i, i + gap - 1] [i + gap , i + 2 * gap - 1]//并回源数组int mid = i + gap - 1;//[i, mid] [mid + 1, i + 2 * gap - 1]if (i + gap < n) {if (i + 2 * gap - 1 >= n) {PartMerge(arr, i, n - 1, mid, tmp);}else {PartMerge(arr, i, i + 2 * gap - 1, mid, tmp);}}}gap *= 2;}free(tmp);
}

归并排序的特性总结:

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

🔆非比较排序

思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。
操作步骤:

  1. 统计相同元素出现次数
  2. 根据统计的结果将序列回收到原来的序列中
    在这里插入图片描述
//计数排序
void CountSort(int* arr, int n) {assert(arr);int max = arr[0], min = arr[0];for (int i = 1; i < n; ++i) {if (arr[i] > max) {max = arr[i];}if (arr[i] < min) {min = arr[i];}}if (max == min) {return;}int sz = n;if ((max - min) > n) {sz = max - min + 1;}int* tmp = (int*)calloc(sz, sizeof(int));assert(tmp);for (int i = 0; i < n; ++i) {(tmp[arr[i] - min])++;}int index = 0;int i = 0;while (i < sz) {if (tmp[i]--) {arr[index++] = i + min;}else {i++;}}free(tmp);
}

计数排序的特性总结:

  1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
  2. 时间复杂度:O(MAX(N,范围))
  3. 空间复杂度:O(范围)
  4. 稳定性:稳定

在这里插入图片描述

🔆结语

到这里这篇博客已经结束啦。
这份博客👍如果对你有帮助,给博主一个免费的点赞以示鼓励欢迎各位🔎点赞👍评论收藏⭐️,谢谢!!!
如果有什么疑问或不同的见解,欢迎评论区留言欧👀

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

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

相关文章

十二、Pytorch复现Residual Block

一、Residual Network 论文出处&#xff1a;Deep Residual Learning for Image Recognition 其核心模块&#xff1a; 二、复现Residual Block 这里以两层卷积层为例进行设计复现 resnet可以很好的解决梯度消失问题 Residual Block大致要点&#xff1a; 样本x传入模型&…

下一个“AI王炸”,别只盯着OpenAI,DeepMind也在憋大招

过去几个月&#xff0c;OpenAI风头无两&#xff0c;各大科技公司争先恐后地跟进大语言模型&#xff08;LLM&#xff09;这一技术路线。对比之下&#xff0c;OpenAI的老对手DeepMind&#xff0c;显得有些低调和沉默。微软靠OpenAI打了一场胜仗&#xff0c;而谷歌推出的Bard翻了车…

5.5G,运营商能接受吗?

2月份&#xff0c;我在巴塞罗那参加MWC 2023时有个发现。欧洲通信设备商大多在宣传和讨论6G相关技术&#xff0c;中国通信设备商却在重点展示5.5G&#xff0c;或者叫做5G-Advanced。而全球各国的运营商则都普遍处在比较观望的状态里&#xff0c;两头考察&#xff0c;多样化尝试…

中间表示- 到达定义分析

基本概念 定义&#xff08;def&#xff09;&#xff1a;对变量的赋值 使用&#xff08;use&#xff09;&#xff1a;对变量值的读取 问题&#xff1a;能把上图中的y替换为3吗&#xff1f;如果能&#xff0c;这称之为“常量传播”优化。 该问题等价于&#xff0c;有哪些对变量y…

OPNET Modeler 例程——创建一个移动无线网络

文章目录一、例程概述二、创建天线模型三、创建指向处理器四、创建节点模型1.发射机节点模型2.干扰发射机节点模型3.收信机节点模型五、创建网络模型六、收集统计量并运行仿真七、查看仿真结果总结一、例程概述 OPNET 无线模块支持地面和卫星无线系统的构建。在此例程中将构建…

【C++】基础篇

C基础篇什么是C命名空间命名空间的三种使用方式C的输入和输出缺省参数缺省参数分类函数重载引用引用的使用场景常引用指针和引用的区别auto关键字auto使用细则auto不能推导的场景基于范围的for循环范围for的使用条件指针空值nullptr什么是C 1982年&#xff0c;Bjarne Stroustr…

微服务+springcloud+springcloud alibaba学习笔记【Eureka服务注册中心】(3/9)

Eureka服务注册中心 3/91、服务注册与发现1.1 什么是服务治理&#xff1a;1.2 什么是服务注册与发现&#xff1a;1.3 Eureka服务注册与发现2、单机版eureka2.1 创建module2.2改pom依赖2.3写yml配置文件:2.4主启动类2.5 修改服务提供者 cloud-provider-payment8001 模块&#xf…

GFS的卷类型与集群实验文档

GlusterFS 支持七种卷&#xff0c;即分布式卷、条带卷、复制卷、分布式条带卷、分布式复制卷、条带复制卷和分布式条带复制卷。我们常用的有前五种&#xff0c;今天我们就来看一看这五种卷都有什么优缺点。 一、分布式卷&#xff08;Distribute volume&#xff09; 文件通过 H…

【模型复现】resnet,使用net.add_module()的方法构建模型。小小的改进大大的影响,何大神思路很奇妙,基础很扎实

从经验来看&#xff0c;网络的深度对模型的性能至关重要&#xff0c;当增加网络层数后&#xff0c;网络可以进行更加复杂的特征模式的提取&#xff0c;所以当模型更深时理论上可以取得更好的结果。但是更深的网络其性能一定会更好吗&#xff1f;实验发现深度网络出现了退化问题…

python玄阶斗技--tkinter事件

在前一篇文章中&#xff0c;我们已经了解是tkinter的一些标签的使用&#xff0c;但一个GUI程序除了让别人看到&#xff0c;还要有一些交互操作&#xff0c;实现人机交互的方法我们称为事件&#xff0c;通过事件分为&#xff1a;鼠标事件&#xff0c;键盘事件和窗口事件。接下来…

Neo4j初学者使用记录(在更)

打开Neo4j cmdR 输入neo4j console 浏览器中输入框中网址&#xff1a;http://localhost:7474/即可打开 新建库 服务器版需要更改配置文件&#xff0c;若neo4j服务正在运行&#xff0c;则按Ctrlc&#xff0c;停止该服务。 配置完后&#xff0c;再重新开启服务&#xff0c;刷新…

如何利用ventoy制作Linux to go (把deepin放到U盘里)

准备工作 最新版本 – 深度科技社区 (deepin.org) deepin镜像官方下载即可 Releases ventoy/vtoyboot GitHub ventoy启动插件选择1.0.29版本 Downloads – Oracle VM VirtualBox VirtualBox虚拟机官网 ventoy下载 VentoyRelease (lanzoui.com) 选择下载1.0.29版本 vento…

第五十八章 线段树(一)

第五十八章 线段树&#xff08;一&#xff09;一、树状数组的缺陷二、线段树的作用三、线段树的基本构成1、节点定义2、线段树的结构四、线段树的重要函数1、构造线段树——bulid函数2、查询区间——query函数3、单点修改——modify函数五、例题一、树状数组的缺陷 在前面两个…

对于电商行业来讲,真正决定它的并不是规模,而是载体

纵然是在现在这样的情况之下&#xff0c;我们依然无法用「格局已定」来形容和阐述现在的电商市场格局。这一点&#xff0c;我们可以从以抖音、快手为代表的电商新势力的崛起当中&#xff0c;看出一丝端倪。对于电商行业来讲&#xff0c;真正决定它的并不是规模&#xff0c;而是…

Dart中的异步

一 事件循环 flutter 就是运行在一个root isolate 中 程序只要运行起来&#xff0c;就有一个事件循环一直在运行 &#xff0c;直至程序退出。 EventLoop 先从mrcro 对列中取任务&#xff0c;取完任务再去 event 队列中取任务。队列任务是FIFO。 二 认识Future abstract clas…

[JavaEE]----Spring03

文章目录Spring_day031&#xff0c;AOP简介1.1 什么是AOP?1.2 AOP作用1.3 AOP核心概念2&#xff0c;AOP入门案例2.1 需求分析2.2 思路分析2.3 环境准备2.4 AOP实现步骤步骤1:添加依赖步骤2:定义接口与实现类步骤3:定义通知类和通知步骤4:定义切入点步骤5:制作切面步骤6:将通知…

C++内存管理(new和delete)

目录 1. new/delete操作内置类型 2. new和delete操作自定义类型 3. operator new与operator delete函数 4 .new和delete的实现原理 1 .内置类型 2 .自定义类型 new的原理 delete的原理 new T[N]的原理 delete[]的原理 5. 定位new表达式(placement-new) 6. malloc/f…

使用Process Explorer和Clumsy定位软件高CPU占用问题

目录 1、问题描述 2、使用Process Explorer初步找到CPU占用高的原因 3、使用Clumsy工具在公司内网环境复现了问题 4、根据Process Explorer中的函数调用堆栈&#xff0c;分析源码&#xff0c;最终找出了问题 5、总结 在排查项目客户的视频图像闪烁问题时&#xff0c;无意中…

Centos7安装部署Jenkins

Jenkins简介&#xff1a; Jenkins只是一个平台&#xff0c;真正运作的都是插件。这就是jenkins流行的原因&#xff0c;因为jenkins什么插件都有 Hudson是Jenkins的前身&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控程序重复的工作&#xff0c;Hudson后来被…

JavaScript基础-02

常量&#xff08;字面量&#xff09;&#xff1a;数字和字符串 常量也称之为“字面量”&#xff0c;是固定值&#xff0c;不可改变。看见什么&#xff0c;它就是什么。 常量有下面这几种&#xff1a; 数字常量&#xff08;数值常量&#xff09;字符串常量布尔常量自定义常量…