【数据结构】特殊矩阵的压缩存储
🎇【数据结构】特殊矩阵的压缩存储🎇

文章目录
- 🎇【数据结构】特殊矩阵的压缩存储🎇
- 🍰一.数组的存储结构
- 🚀1.数组的定义
- 🚀2.数组的存储结构
 
- 🍰二.特殊矩阵的存储结构
- 🚀1.普通矩阵的存储
- 🚀2.特殊矩阵的压缩存储
- 🔆1.对称矩阵
- 🔆2.三角矩阵
- 🔆3.三对角矩阵
- 🔆4.稀疏矩阵
 
 
🍰一.数组的存储结构
🚀1.数组的定义
数组是由n个相同类型的数据元素所构成的有限序列
数组和线性表的关系:
 数组是线性表的推广:一维数组可以看做是一个线性表,而对于二维数组而言,可以看成是有多个线性表组成的线性表
也就是每一行 / / /列视都为一个线性表,总的线性表内每一个元素也是一个定长的线性表

🚀2.数组的存储结构
- 对于一维数组的存储,就是线性表,一维数组的所有元素在内存空间中占用一段连续的内存空间

那么,对于多维数组的存储,在计算机中是如何实现的呢?
- 对于多维数组的存储,在计算机中仍表现为一维数组的形式,也就是一段连续的内存空间
  
接下来,我们就要去尝试模拟计算机存放多维数组的过程:
有两种映射方式:行优先和列优先
①行优先:
以二维数组为例,以行优先存储的方式为:也就是逐行放入一维数组中
 
 
🌈 下标转换关系:
(我们默认下标从0开始)对于二维数组  A [ N ] [ M ] A[N][M] A[N][M] 中的元素  a i j a_{ij} aij ,若希望在行优先转化后的一维数组中访问它,我们可以确定其在一维数组中的下标为:
  
分解代码实现:
- 行优先二维转一维数组
void row_Reducedim(int a[][M],int *res, int row, int col)
{int p = 0;for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {res[p++] = a[i][j];}}
}
- 按照索引从一维数组中取值
void visit(int res[], int i, int j)
{if (i <= N-1 && i >= 0 && j >= 0 && j <= M-1){int k = i * M + j;cout << res[k] << endl;}elsecout << "输入违规" << endl;
}
行优先完整代码实现:
#include<iostream>
using namespace std;void row_Reducedim(int a[][4],int *res, int row, int col)
{int p = 0;for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {res[p++] = a[i][j];}}
}void Print(int a[], int n)
{for (int i = 0; i < n; i++){cout << a[i] << " ";}cout << endl;
}void visit(int res[], int i, int j)
{if (i <= 1 && i >= 0 && j >= 0 && j <= 3){int k = i * 4 + j;cout << res[k] << endl;}elsecout << "输入违规" << endl;
}int main() {int martix[2][4] ={ {1,2,3,4},{5,6,7,8} };int res[8];//二维转一维row_Reducedim(martix,res, 2, 4); //行优先cout << "行转化后的一维数组为:" << endl;Print(res,8);//二维数组元素在一维数组内的映射int i, j;cout << "对于行转换后的矩阵,输入要访问的元素在二维数组中的行,列下标数(>=0)" << endl;cin >> i >> j;visit(res, i, j);system("pause");return 0;
}
②列优先:
以二维数组为例,以行优先存储的方式为:也就是逐列放入一维数组中

🌈 下标转换关系:
对于二维数组  A [ N ] [ M ] A[N][M] A[N][M] 中的元素  a i j a_{ij} aij ,其在一维数组中的下标为:
  
分解代码实现:
- 列优先二维转一维数组
void col_Reducedim(int a[][4], int* res1, int row, int col)
{int p = 0;for (int j = 0; j < col; j++) {for (int i = 0; i < row; i++) {res1[p++] = a[i][j];}}
}
- 按照索引从一维数组中取值
void visit1(int res[], int i, int j)
{if (i <= 1 && i >= 0 && j >= 0 && j <= 3){int k = j * 2 + i;cout << res[k] << endl;}elsecout << "输入违规" << endl;
}
列优先完整代码实现:
#include<iostream>
using namespace std;void col_Reducedim(int a[][4], int* res1, int row, int col)
{int p = 0;for (int j = 0; j < col; j++) {for (int i = 0; i < row; i++) {res1[p++] = a[i][j];}}
}void Print(int a[], int n)
{for (int i = 0; i < n; i++){cout << a[i] << " ";}cout << endl;
}void visit1(int res[], int i, int j)
{if (i <= 1 && i >= 0 && j >= 0 && j <= 3){int k = j * 2 + i;cout << res[k] << endl;}elsecout << "输入违规" << endl;
}int main() {int martix[2][4] ={ {1,2,3,4},{5,6,7,8} };int res1[8];//二维转一维col_Reducedim(martix, res1, 2, 4); //列优先cout << "列转化后的一维数组为:" << endl;Print(res1, 8);int a, b;cout << "对于列转换后的矩阵,输入要访问的元素在二维数组中的行,列下标数(>=0)" << endl;cin >> a >> b;visit1(res1, a, b);system("pause");return 0;
}
输出结果:

🍰二.特殊矩阵的存储结构
🚀1.普通矩阵的存储
对于普通的矩阵,我们可以将其视为二维数组进行处理,也就是按行优先和列优先的方式存储,在之前也已经提到过

🚀2.特殊矩阵的压缩存储
压缩存储:指为多个值相同的元素所分配一个存储空间,对零元素不分配存储空间,用于节省空间
接下来,我们来看几个典型的特殊矩阵:
🔆1.对称矩阵
对于一个n阶的方阵,我们可以将其划分为主对角线,上三角区和下三角区,而对于对称矩阵来说,有 a i j = a j i a_{ij}=a_{ji} aij=aji,因此,若仍然采用二维数组存储,会造成一半的空间浪费
策略:
因此,我们其实只需要 存主对角线和下三角块 即可:
 
🌊 对称矩阵与存储后一维数组的映射关系:
我们规定矩阵元素的下标 i , j i,j i,j的范围为 [ 1 , n ] [1,n] [1,n],而一维数组的下标默认是从0开始的

- 一维数组大小:
 第一行:一个元素, a 11 a_{11} a11
 第二行:两个元素, a 21 , a 22 a_{21},a_{22} a21,a22
 …
 共有 n n n 行,则元素总数 k = n ∗ ( n + 1 ) / 2 k=n*(n+1)/2 k=n∗(n+1)/2

-  压缩存储后的访问: 又回到了,压缩完成之后,我们应该如何获取这些数据呢? 我们只需要定义一个映射函数即可: 

策略:
不难发现,如果我们希望取出二维数组内的元素 a i j a_{ij} aij,我们已知了它的行号和列号:
①先看行向:
 在 a i j a_{ij} aij上面的元素(即前 i − 1 i-1 i−1行)的元素个数为:
  
②再看列向:
 在 a i j a_{ij} aij前面的元素(即前 j − 1 j-1 j−1行)的元素个数为:
  
由于一维数组下标是从 0 0 0开始的,因此: a i j 的一维数组下标 = 在 a i j 前的元素个数 a_{ij}的一维数组下标=在a_{ij}前的元素个数 aij的一维数组下标=在aij前的元素个数
再由 a i j = a j i a_{ij}=a_{ji} aij=aji,因此,映射函数为:

完整代码实现:
#include<iostream>
using namespace std;//打印下三角
void triangle(int a[][3], int* res, int row, int col)
{int p = 0;for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {if (i >= j)res[p++] = a[i][j];}}
}//访问
void visit(int res[], int i, int j)
{if (i >= j){int k = i * (i - 1) / 2 + j - 1;cout << res[k] << endl;}else{int k = j * (j - 1) / 2 + i - 1;cout << res[k] << endl;}
}//打印一维数组
void Print(int a[], int n)
{for (int i = 0; i < n; i++){cout << a[i] << " ";}cout << endl;
}int main() {int martix[3][3] ={	{1,2,3},{2,1,2},{3,2,1} };int res[6];triangle(martix, res, 3, 3);cout << "下三角的一维数组为:" << endl;Print(res,6);int i, j;cout << "请输入需要访问的元素aij中的下标i,j(>=1):" << endl;cin >> i >> j;visit(res, i, j);system("pause");return 0;
}
输出结果:

🔆2.三角矩阵
三角矩阵就是有一个三角区全为常量的矩阵

策略:
其储存思维和对称矩阵类似,不同之处就在于:
✅ 存储完下三角区和主对角线后,紧接着存储对角线上方的常量,也就是要在对称矩阵构造的一维数组后面添加一个常数项

-  一维数组的大小: k = n ∗ ( n + 1 ) / 2 k=n*(n+1)/2 k=n∗(n+1)/2 
-  映射函数为: 

完整代码实现:
#include<iostream>
using namespace std;//打印下三角
void triangle(int a[][3], int* res, int row, int col,int n)
{int p = 0;for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {if (i >= j)res[p++] = a[i][j];}}//存常量int c = a[0][n-1];res[n * (n + 1) / 2] = c;
}//访问
void visit(int res[], int i, int j,int n)
{if (i >= j){int k = i * (i - 1) / 2 + j - 1;cout << res[k] << endl;}else{int k = n * (n + 1)/2;cout << res[k] << endl;}
}//打印一维数组
void Print(int a[], int n)
{for (int i = 0; i < n; i++){cout << a[i] << " ";}cout << endl;
}int main() {int martix[3][3] ={ {1,4,4},{2,1,4},{3,2,1} };int res[6];triangle(martix, res, 3, 3,3);cout << "下三角的一维数组为:" << endl;//加一个常数项Print(res, 6+1);int i, j;cout << "请输入需要访问的元素aij中的下标i,j(>=1):" << endl;cin >> i >> j;visit(res, i, j,3);system("pause");return 0;
}
输出结果:

🔆3.三对角矩阵
三对角矩阵也称带状矩阵,对于n阶方阵的A中的任一元素 a i j a_{ij} aij,当 ∣ i − j ∣ > 1 |i-j|>1 ∣i−j∣>1时, a i j = 0 a_{ij}=0 aij=0

策略:
将三条对角线上的元素按行优先原则存放在一维数组中(零元素不存)

-  一维数组的大小: 
 由于只有第一行和最后一行只有两个元素,因此,一维数组大小为:
 l e n = 3 n − 2 len=3n-2 len=3n−2 
-  映射函数为: 
 由于前 i − 1 i-1 i−1行有 3 i − 1 3i-1 3i−1个元素,当前行前面有 j − i + 1 j-i+1 j−i+1个元素
 k = 2 i + j − 3 k=2i+j-3 k=2i+j−3 
反之,若我们已知 a i j a_{ij} aij存放于一维数组中的第 k个位置,怎么推出行数和列数呢?

i = ⌈ ( k + 2 ) / 3 ⌉ i=⌈(k+2)/3⌉ i=⌈(k+2)/3⌉ 再由公式 k = 2 i + j − 3 k=2i+j-3 k=2i+j−3可以推出: j = k − 2 i + 3 j=k-2i+3 j=k−2i+3
完整代码实现:
#include<iostream>
#include<math.h>
using namespace std;//转一维矩阵
void Three(int a[][4], int* res, int row, int col)
{int p = 0;for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {if (a[i][j] != 0)res[p++] = a[i][j];}}
}void Print(int a[],int n) {for (int i = 0; i < n; i++) {cout << a[i] << " ";}cout << endl;
}void visit1(int a[], int i, int j)
{int k = 2*i + j - 3;printf("a%d%d=%d\n", i,j,a[k]);
}void visit2(int a[], int k)
{int i, j;i = ceil((k + 2) / 3);j = k - 2 * i + 3;printf("访问的元素为:a%d%d\n", i, j);
}int main() {int martix[4][4] ={{1,2,0,0},{1,2,3,0},{0,2,3,4},{0,0,3,4}};int res[10];//转一维Three(martix, res, 4, 4);cout << "三对角的一维矩阵为:" << endl;Print(res, 10);//正向访问int i, j;cout << "请输入需要访问的元素aij中的下标i,j(>=1):" << endl;cin >> i >> j;visit1(res, i, j);//反向访问int k;cout<<"请输入该元素在一维数组中的下标:" << endl;cin >> k;visit2(res, k);system("pause");return 0;
}
输出结果:

🔆4.稀疏矩阵
还有一种特殊矩阵,其矩阵内的非0元素远远少于零元素,则称其为稀疏矩阵
 e . g e.g e.g 如下矩阵:
 
| 我们只存取非零元素,但其分布往往没有规律,因此,我们还应该记录它的位置 | 
策略:
将非零元素的行,列,值构成一个三元组 (行标,列标,值) (行标,列标,值) (行标,列标,值)
✅ 我们用结构体定义这个三元组:
#define Maxsize 100//结构体定义三元组
typedef struct {int i;int j;int val;
}Triple[Maxsize]; //结构体数组

完整代码实现:
#include<iostream>
#define Maxsize 100
using namespace std;//结构体定义三元组
typedef struct {int i;int j;int val;
}Triple[Maxsize]; //结构体数组//稀疏矩阵转三元组
void triple(Triple &T,int a[][5],int row,int col,int &p)
{for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {if (a[i][j] != 0) {T[p].i = i+1;T[p].j = j+1;T[p].val = a[i][j];p++;}}}
}void Print(Triple &T,int len) {for (int i = 0; i < len; i++) {printf("a%d%d=",T[i].i,T[i].j);cout << T[i].i<< " " << T[i].j << " " << T[i].val << endl;}
}int main() {int martix[5][5] ={{0,2,0,0,0},{0,0,1,0,2},{3,0,0,0,1},{0,5,0,0,0},{1,0,4,0,0}};Triple T;int p = 0;//稀疏矩阵转三元组triple(T, martix, 5, 5, p);cout << "转化后的三元组为(矩阵元素下标从1开始):\n" << endl;Print(T, p);system("pause");return 0;
}
输出结果:



相关文章:
 
【数据结构】特殊矩阵的压缩存储
🎇【数据结构】特殊矩阵的压缩存储🎇 🌈 自在飞花轻似梦,无边丝雨细如愁 🌈 🌟 正式开始学习数据结构啦~此专栏作为学习过程中的记录🌟 文章目录 🎇【数据结构】特殊矩阵的压缩存储Ἰ…...
 
在layui中使用vue,使用vue进行页面数据部分数据更新
layui是一款非常优秀的框架,使用也非常的广泛,许多后台管理系统都使用layui,简单便捷,但是在涉及页面部分数据变化,就比较难以处理,比如一个页面一个提交页,提交之后部分数据实时进行更新&#…...
 
Vue中如何进行数据导入与Excel导入
Vue中如何进行数据导入与Excel导入 Vue是一款非常流行的JavaScript框架,它提供了一套用于构建用户界面的工具和库。在Vue中,我们可以使用多种方式来导入数据,包括从服务器获取数据、从本地存储获取数据、从文件中读取数据等等。其中…...
 
git 的基本操作
1. git建立本地仓库 在想要建立的目录下输入命令 git init 我们可以看一下 .git目录下有什么 2. 配置git本地仓库 配置用户的 name 和 email 命令:git config [...] 配置完后,我们像查看一下 刚才的配置 2.1 查看配置命令 git config -l 2.2 删除…...
 
搭建Vue项目以及项目的常见知识
前言:使用脚手架搭建vue项目,使用脚手架可以开发者能够开箱即用快速地进行应用开发而开发。 搭建 #创建一个基于 webpack 模板的新项目 vue init webpack my-project #选择所需要的选项如图: cd my-project npm run dev访问localhost:808…...
 
TypeScript ~ TS Webpack构建工具 ⑦
作者 : SYFStrive 博客首页 : HomePage 📜: TypeScript ~ TS 📌:个人社区(欢迎大佬们加入) 👉:社区链接🔗 📌:觉得文章不错可以点点关注 &…...
Rust 自建HTTP Server支持图片响应
本博客是在杨旭老师的 rust web 全栈教程项目基础上进行修改,支持了图片资源返回,杨旭老师的rust web链接如下: https://www.bilibili.com/video/BV1RP4y1G7KFp1&vd_source8595fbbf160cc11a0cc07cadacf22951 本人默认读者已经学习了相关…...
[游戏开发][Unity]UnityWebRequest使用大全
首先记录个小问题 使用new UnityWebRequest的方式,最终的downloadHandler是个null 使用UnityWebRequest.Get的方式,最终的downloadHandler会是DownloadHandlerBuffer 从网站或本地下载内容,包括文本或二进制数据 IEnumerator downloadfile(st…...
 
如何使用Fiddler对手机进行弱网测试?(干货教程)
1.首先,fiddler连接手机 1)Tools->Options->Connections->设置端口8888,勾选Allow remote computers to connect 2)配置手机 注:手机和电脑需要在同一局域网下 手机进入网络详情,将代理改为手动 设置主机名、端口 主机…...
 
专业科普:什么是单片机?
一、什么是单片机 单片机诞生于20世纪70年代末,它是指一个集成在一块芯片上的完整计算机系统。单片机具有一个完整计算机所需要的大部分部件:CPU、内存、内部和外部总线系统,目前大部分还会具有外存。同时集成诸如通讯接口、定时器ÿ…...
 
深度学习-第T11周——优化器对比实验
深度学习-第T11周——优化器对比实验 深度学习-第T11周——优化器对比实验一、前言二、我的环境三、前期工作1、导入数据集2、查看图片数目3、查看数据 四、数据预处理1、 加载数据1、设置图片格式2、划分训练集3、划分验证集4、查看标签 2、数据可视化3、检查数据4、配置数据集…...
 
基于Dlib的疲劳检测系统
需要源码的朋友可以私信我 基于Dlib的疲劳检测系统 1、设计背景及要求2、系统分析3、系统设计3.1功能结构图3.2基于EAR、MAR和HPE算法的疲劳检测3.2.1基于EAR算法的眨眼检测3.2.2基于MAR算法的哈欠检测3.3.3基于HPE算法的点头检测 4、系统实现与调试4.1初步实现4.2具体实现过程…...
 
three.js通过CubeTexture加载环境贴图,和RGBELoader加载器加载hdr环境贴图
一、使用CubeTexture进行环境贴图 1.CubeTexture使用介绍 Three.js中可以通过使用CubeTexture进行环境贴图,CubeTexture需要将6张图片(正面、反面、上下左右)包装成一个立方体纹理。下面是一个简单的例子: 首先需要加载六张贴图…...
 
pycharm中Terminal输入sqlite3,出现无法将sqlite项识别为cmdlet**的解决方法
前提:本机上已安装sqlite3,安装详见:pycharm社区版中安装配置sqlite3_Sunshine_0426的博客-CSDN博客 问题: cmd命令行中或pycharm中Terminal行输入sqlite3 db.sqlite3命令后,出现“无法将“sqlite3”项识别为 cmdlet…...
 
VSCode 安装配置教程详解包含c++环境配置方法
vscode安装教程及c环境配置详解 vscode下载安装下载C扩展插件VScode C环境配置配置环境变量检查 MinGW 安装配置编译器:配置构建任务检查是否安装了编译器配置完毕 vscode下载安装 地址:官网下载地址 直接打开下载好的.exe文件进行安装即可࿰…...
Baumer工业相机堡盟工业相机如何通过BGAPISDK将图像放大缩小显示(C#)
Baumer工业相机堡盟工业相机如何通过BGAPISDK将图像放大缩小显示(C#) Baumer工业相机Baumer工业相机BGAPISDK和图像放大缩小的技术背景Baumer工业相机通过BGAPISDK将相机图像图像放大缩小功能1.引用合适的类文件2.通过BGAPISDK将相机图像图像放大缩小功能…...
 
8.1 PowerBI系列之DAX函数专题-进阶-解决列排序对计算的影响
需求 下列矩阵中,在月份列不按照原始数据的month_no排列时,能正确计算销售额占比,但是当月份按照month_no排序时就会出错,需要解决这个问题。 实现 month % divide([amount],calculate([amount],all(date[month desc]))) //排…...
 
Java的第十二篇文章——集合
目录 第十二章 集合 学习目标 1. 集合框架的由来 2. 集合框架的继承体系 3. Collection接口 3.1 Collection接口的常用方法 4. Iterator接口 4.1 Iterator接口的抽象方法 4.2 获取迭代器接口实现类 4.3 迭代器的实现原理 4.4 并发修改异常 4.5 集合存储自定义对象并…...
 
docker 镜像制作 与 CI/CD
目录 镜像到底是什么? 使用docker创建镜像 步骤: 1、编辑Dockerfile(Dockerfile是docker制作镜像的配方文件) 2、编辑requirements.txt文件 3、编辑app.py文件,我们的程序文件 4、生成镜像文件 5、查看生成的镜…...
 
Spring Boot 异常报告器解析
基于Spring Boot 3.1.0 系列文章 Spring Boot 源码阅读初始化环境搭建Spring Boot 框架整体启动流程详解Spring Boot 系统初始化器详解Spring Boot 监听器详解Spring Boot banner详解Spring Boot 属性配置解析Spring Boot 属性加载原理解析Spring Boot 异常报告器解析 创建自定…...
 
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
 
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
 
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
 
Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
 
[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG
TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码:HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...
 
【无标题】湖北理元理律师事务所:债务优化中的生活保障与法律平衡之道
文/法律实务观察组 在债务重组领域,专业机构的核心价值不仅在于减轻债务数字,更在于帮助债务人在履行义务的同时维持基本生活尊严。湖北理元理律师事务所的服务实践表明,合法债务优化需同步实现三重平衡: 法律刚性(债…...
 
实战设计模式之模板方法模式
概述 模板方法模式定义了一个操作中的算法骨架,并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤。简单来说,就是在一个方法中定义了要执行的步骤顺序或算法框架,但允许子类…...
