当前位置: 首页 > news >正文

12.18构建哈夫曼树(优先队列),图的存储方式,一些细节(auto,pair用法,结构体指针)

为结构体自身时,用.调用成员变量;为结构体指针时,用->调用成员变量

所以存在结构体数组时,调用数组元素里的成员变量,就是要用.

结构体自身只有在new时才会创建出来,而其指针可以随意创建

在用new时,要返回指向结构体的指针

构建哈夫曼树

哈夫曼树是在叶子节点和权重确定的情况下,带权路径最小的二叉树,也被称为最优二叉树

基本思路就是先将每个给定权值的节点看成一颗只有根节点的树,然后不断合成权值最小的两个树,生成一个权值为他们之和的一颗新树,最终剩下的一棵树就是哈夫曼树

如果有N个节点,就要迭代N-1轮

优先队列

注意队列的队头函数是front,优先队列是top,栈也是top

大顶堆是一种特殊的二叉堆,其中每个父节点的值都大于或等于其子节点的值。因此,大顶堆是按照降序排列的,即根节点的值最大,而子节点的值逐渐减小。

//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;

//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了) 

即需要注意优先队列的第三个参数传入的并不是函数,而是一个结构体

    priority_queue<int, vector<int>, less<int>>a;//定义了降序排列的优先队列,即队头最大,从队头到队尾逐渐减小
    priority_queue<int, vector<int>, greater<int>>a;//小顶堆,从队头到队尾升序排列

第二个参数只用写类型即可

struct tmp1 {int x;tmp1(int a) { x = a; }//:x(a){}bool operator<(const tmp1& a)const {return x < a.x;}
};//运算符重载
//这里就是直接在存储的类型里,然后重载运算符,这个调用优先队列时,只需要一个参数,即要存储的结构体即可
//因为结构体里就已经包装了比较的方法
struct tmp2 {bool operator()(tmp1 a, tmp1 b) {return a.x < b.x;}
};//写仿函数,就是写一个比较函数,不过是包装成结构体,
//需要注意这里是额外写的一个结构体,所以在优先队列里定义的时候,要第三个参数,写到第三个参数里
//然后第一个参数是存储的类型,第二个是vector<相应类型>,表示存储的容器

在STL中,如果你使用的是标准的std::priority_queue容器,那么需要定义的仿函数的确是要重载函数调用运算符operator(),因为std::priority_queue默认使用std::lessstd::greater作为仿函数,它们都是通过重载函数调用运算符来实现比较的。

即写仿函数时,只能重载运算符()

这个第三个参数就是代表从队头到队尾满足一个怎样的关系。队头就是堆顶元素

为什么less构建出大顶堆

想的是类似于自定义sort排序方式,排序都按照规定的<与>方向进行排序

但优先队列相反,用<时,头部是最大的;用>时,头部是最小的

根因在于其底层实现。

less:左数小于右数时,返回true,否则返回false。

在堆的调整过程中,对于大顶堆,如果当前插入的节点值大于其父节点,那么就应该向上调整其父节点索引小于当前插入节点的索引,也就是父节点是左数,插入节点是右值,可以看到,左数小于右数时,要向上调整,也就是Compare函数应该返回true,正好是less。

(而对于顺序存储,左数右数就是直观理解,没有固定,这里是堆,所以就固定先前的位置为左数,要比较的是右数,如果先前固定的位置和比较的位置满足关系,就要交换,返回true不然则不动)
priority_queue传入的第三个参数是仿函数,是将新插入数据与父结点进行比较

原本是:if (_con[child] > _con[parent])

使用仿函数:if (com(_con[child] , _con[parent]))

(调用仿函数是这个形式,所以仿函数结构体里只能重载())

这里记住,是将父结点与子结点进行比较,满足条件(置true才进行交换)就很容易记住哪个对应大顶堆,哪个对应小顶堆了

就是说仿照堆的构建过程,新插入的直接和最大的比较,而不是连续的顺序存储,从小比到大,类似于插入排序

细节

只有string为length

返回数据结构的大小用的都是size(),只有string类用的是Length() 

数对pair

vector可以存储数对,但对于数对,必须要在数对<>前标明pair,直接写为这样会报错

应该写为

auto的应用

auto返回的就是数据结构内部储存的数据的类型

     for 循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。 

std::map<std::string, int> scores = {{"Alice", 90}, {"Bob", 80}, {"Charlie", 95}};for (auto it = scores.begin(); it != scores.end(); ++it) {auto& name = it->first;auto& score = it->second;// 使用 name 和 score 进行操作
}

 

huf* root = bulid(weights, chars);vector<hfcode>code;generatecod(root, "", code);for (auto hc : code) {cout << hc.ch << " : " << hc.code << endl;}

总代码

struct huf {int w;char ch;huf* left, * right;huf(int w, char c = '\0', huf* l = nullptr, huf* r = nullptr) :w(w), ch(c), left(l), right(r) {}
};
struct hfcode {char ch;//ch是原本的字符值string code;//这个是编码后的结果//本质上就是一个pair对hfcode(char c = '\0', string s = "") :ch(c), code(s) {}
};
struct cmp {bool operator()(huf* a, huf* b) { return a->w > b->w; }
};//如果大于了就交换,返回true,说明新换上来的比较小,那么堆顶元素就保持为最小的
huf* build(vector<int>& weights, vector<char>& chars) {priority_queue<huf*, vector<huf*>, cmp>pq;for (int i = 0; i < weights.size(); i++) {pq.push(new huf(weights[i], chars[i]));}while (pq.size() > 1) {huf* left = pq.top();pq.pop();huf* right = pq.top();pq.pop();huf* parent = new huf(left->w + right->w, '\0', left, right);pq.push(parent);}return pq.top();
}
void generatecod(huf* root, string code, vector<hfcode>& hufcode) {//目的是要得到每个叶子节点的哈夫曼编码,这个vecotr数组记录的是hfcode结构体,就是原始数据以及编码的一个数对if (!root)return;if (root->ch != '\0') {//如果是\0就表明是非叶子节点,不是就表明是叶子节点,是需要编码的节点,所以就进行hufcode.push_back(hfcode(root->ch, code));}generatecod(root->left, code + "0", hufcode);generatecod(root->right, code + "1", hufcode);
}
vector<int>weights = { 5,2,7,4,9 };
vector<char>chars = { 'A', 'B', 'C', 'D', 'E' };
huf* root = bulid(weights, chars);
vector<hfcode>code;
generatecod(root, "", code);
for (auto hc : code) {cout << hc.ch << " : " << hc.code << endl;
}

 

#include<iostream>
#include<queue>
#include<vector>
#include<string>
using namespace std;
struct node {int w;//表示节点的权重,为叶子节点时表示出现的频次char ch;//表示节点的编号,名称node* left, * right;node(int w, char c = '\0', node* l = nullptr, node* r = nullptr) :w(w), ch(c), left(l), right(r) {}//含参,默认,构造函数,c为\0时表示非叶子节点,无实际意义,w为不含参,需要实际传值
};
struct cmp {bool operator()(node* a, node* b) {return a->w < b->w;}
};
node* build(vector<int>ws, vector<char>cs) {priority_queue<node*, vector<node*>, cmp>pq;for (int i = 0; i < ws.size(); i++) {node* n = new node(ws[i], cs[i]);pq.push(n);//将所有的叶子节点加入到优先队列当中}while (pq.size() > 1) {node* l = pq.top();pq.pop();node* r = pq.top();pq.pop();node* parent = new node(l->w + r->w, '\0', l, r);pq.push(parent);}return pq.top();//最后返回构建好的哈夫曼树的根节点指针
}
void code(node* root, string c, vector <pair< char, string >> &a) {if (!root)return;if (root->ch != '\0') {a.push_back(make_pair(root->ch, c));}else {code(root->left, c + '0', a);code(root->right, c + '1', a);}
}
int main() {vector<int>w = { 5,2,7,4,9 };vector<char>chars = { 'A', 'B', 'C', 'D', 'E' };node* root = build(w, chars);vector<pair<char, string>>d;code(root, "", d);for (auto dp : d) {cout << dp.first << ":" << dp.second << endl;}return 0;
}

调试过程

	vector<int>webuild = { 5,2,7,4,9 };vector<char>chars = { 'A', 'B', 'C', 'D', 'E' };priority_queue < pair<int, char>, vector<pair<int, char>>, cmp1>p;for (int i = 0; i < weights.size(); i++) {p.push(make_pair(weights[i], chars[i]));}while (!p.empty()) {cout << p.top().second << ":" << p.top().first << endl;p.pop();}struct cmp1 {bool operator()(pair<int, char>a, pair<int, char>b) {return a.first < b.first;}
};

图的存储方式

struct arc {int target;//边里需要记录自己的目标arc* nextarc;int w;//如果有需要,则记录边的权重
};
struct node {int info;//并非节点编号,而是节点自身的某些信息,比如名称,大小arc* firstarc;
};
struct graph {int vnum, anum;node v[20];//在此定义节点编号
};
void creat(graph& g) {cin >> g.vnum >> g.anum;for (int i = 0; i < g.vnum; i++) {cin >> g.v[i].info;//按输入顺序定义节点编号g.v[i].firstarc = nullptr;}for (int i = 0; i < g.anum; i++) {int v1, v2;cin >> v1 >> v2;arc* p1 = new arc;p1->target = v2;p1->nextarc = g.v[v1].firstarc;g.v[v1].firstarc = p1;//若为无向图,还需在v2中做修改arc* p2 = new arc;p2->target = v1;p2->nextarc = g.v[v2].firstarc;g.v[v2].firstarc = p2;}
}

边要记录自己的终点,以及同一起点下边的下一边的索引指针,需要的话还可以记录权值;

边记录的终点,是终点节点的下标标号;点记录的边,是第一条边的索引指针

点要记录以自己为起点的第一条边的索引指针,若要遍历以该边为起点的所有边,用第一条边的后继指针来实现

得到每个点的入度

struct arc {int w;int target;arc* nextarc;
};
struct node {int index;arc* firstarc;
};
struct graph {int vnum, anum;node v[20];
};
int d[20];//记录入度
void countd(graph& g) {//i表示要查询的节点编号for (int i = 0; i < g.vnum; i++) {d[i] = 0;}for (int i = 0; i < g.vnum; i++) {for (arc* item = g.v[i].firstarc; item != nullptr; item = item->nextarc) {d[item->target]++;}}
}

相关文章:

12.18构建哈夫曼树(优先队列),图的存储方式,一些细节(auto,pair用法,结构体指针)

为结构体自身时&#xff0c;用.调用成员变量&#xff1b;为结构体指针时&#xff0c;用->调用成员变量 所以存在结构体数组时&#xff0c;调用数组元素里的成员变量&#xff0c;就是要用. 结构体自身只有在new时才会创建出来&#xff0c;而其指针可以随意创建 在用new时&…...

《Python》面试常问:深拷贝、浅拷贝、赋值之间的关系(附可变与不可变)【用图文讲清楚!】

背景 想必大家面试或者平时学习经常遇到问python的深拷贝、浅拷贝和赋值之间的区别了吧&#xff1f;看网上的文章很多写的比较抽象&#xff0c;小白接收的难度有点大&#xff0c;于是乎也想自己整个文章出来供参考 可变与不可变 讲深拷贝和浅拷贝之前想讲讲什么是可变数据类型…...

使用PE信息查看工具和Dependency Walker工具排查因为库版本不对导致程序启动报错问题

目录 1、问题说明 2、问题分析思路 3、问题分析过程 3.1、使用Dependency Walker打开软件主程序&#xff0c;查看库与库的依赖关系&#xff0c;查看出问题的库 3.2、使用PE工具查看dll库的时间戳 3.3、解决办法 4、最后 VC常用功能开发汇总&#xff08;专栏文章列表&…...

Python编程题目答疑「Python一对一辅导考试真题解析」

你好&#xff0c;我是悦创。 待会更新&#xff5e; 更新计划 答案 题目 记得点赞收藏&#xff01; 题目 之后更新 Solution Question 1 # 读取输入 a float(input("请输入实数 a: ")) b float(input("请输入实数 b: ")) c float(input("请输…...

Python---搭建Python自带静态Web服务器

1. 静态Web服务器是什么&#xff1f; 可以为发出请求的浏览器提供静态文档的程序。 平时我们浏览百度新闻数据的时候&#xff0c;每天的新闻数据都会发生变化&#xff0c;那访问的这个页面就是动态的&#xff0c;而我们开发的是静态的&#xff0c;页面的数据不会发生变化。 …...

在服务器上部署SpringBoot项目jar包

以下是在服务器上部署Spring Boot项目jar包的步骤&#xff1a; 打包项目&#xff1a; 使用IDEA或者命令行工具&#xff08;如Maven或Gradle&#xff09;将Spring Boot项目打包为一个可执行的jar文件。如果使用Maven&#xff0c;可以在项目的根目录下运行以下命令来打包项目&…...

[python]python实现对jenkins 的任务触发

目录 关键词平台说明背景一、安装 python-jenkins 库二、code三、运行 Python 脚本四、注意事项 关键词 python、excel、DBC、jenkins 平台说明 项目Valuepython版本3.6 背景 用python实现对jenkins 的任务触发。 一、安装 python-jenkins 库 pip install python-jenkin…...

Python生成圣诞节贺卡-代码案例剖析【第18篇—python圣诞节系列】

文章目录 ❄️Python制作圣诞节贺卡&#x1f42c;展示效果&#x1f338;代码&#x1f334;代码剖析 ❄️Python制作圣诞树贺卡&#x1f42c;展示效果&#x1f338;代码&#x1f334;代码剖析&#x1f338;总结 &#x1f385;圣诞节快乐&#xff01; ❄️Python制作圣诞节贺卡 …...

深度剖析Ajax实现方式(原生框架、JQuery、Axios,Fetch)

Ajax学习 简介&#xff1a; ​ Ajax 代表异步 JavaScript 和 XML&#xff08;Asynchronous JavaScript and XML&#xff09;的缩写。它指的是一种在网页开发中使用的技术&#xff0c;通过在后台与服务器进行数据交换&#xff0c;实现页面内容的更新&#xff0c;而无需刷新整个…...

任天堂,steam游戏机通过type-c给VR投屏与PD快速充电的方案 三type-c口投屏转接器

游戏手柄这个概念&#xff0c;最早要追溯到二十年前玩FC游戏的时候&#xff0c;那时候超级玛丽成为了许多人童年里难忘的回忆&#xff0c;虽然长大了才知道超级玛丽是翻译错误&#xff0c;应该是任天堂的超级马里奥&#xff0c;不过这并不影响大家对他的喜爱。 当时FC家用机手柄…...

Flink系列之:Checkpoints 与 Savepoints

Flink系列之&#xff1a;Checkpoints 与 Savepoints 一、概述二、功能和限制 一、概述 从概念上讲&#xff0c;Flink 的 savepoints 与 checkpoints 的不同之处类似于传统数据库系统中的备份与恢复日志之间的差异。 Checkpoints 的主要目的是为意外失败的作业提供恢复机制。 …...

【优质书籍推荐】LoRA微调的技巧和方法

大家好&#xff0c;我是爱编程的喵喵。双985硕士毕业&#xff0c;现担任全栈工程师一职&#xff0c;热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。…...

Linux一行命令配置jdk环境

使用方法&#xff1a; 压缩包上传 到/opt, 更换命令中对应的jdk包名即可。 注意点&#xff1a;jdk-8u151-linux-x64.tar.gz 解压后名字是jdk1.8.0_151 sudo tar -zxvf jdk-8u151-linux-x64.tar.gz -C /opt && echo export JAVA_HOME/opt/jdk1.8.0_151 | sudo tee -a …...

从0开始刷剑指Offer

剑指Offer题解 剑指 Offer 11. 旋转数组的最小数字 思路: 二分O(logn) class Solution {public int stockManagement(int[] stock) {int l 0;int r stock.length - 1;while(l < r && stock[0] stock[r]) r --;if(stock[r] > stock[l]) return stock[0];whi…...

使用Java语言中的算法输出杨辉三角形

一、算法思想 创建一个名为YanghuiTest的类,然后创建二维数组&#xff0c;然后遍历二维数组的第一层&#xff0c;然后初始化第二层数组的大小&#xff0c;然后遍历第二层数组&#xff0c;然后将两侧的数组元素赋为1&#xff0c;然后其它数值通过公式计算&#xff0c;最后可以输…...

人工智能_机器学习071_SVM支持向量机_人脸识别算法_LFW人脸数据加载_与理解---人工智能工作笔记0111

然后我们继续来看 这里有个lfw_home可以看到这个数据是,包含了人脸数据 然后我们继续看,在我们的顶你用户目录下,如果安装了,sklearn就会有这样一个目录, scikit_learn_data目录,这个里面可以看到 可以看到这个文件夹中有个 lfw_home文件夹是对.zip文件夹的解压,这个下载以后…...

Java 8中流Stream API详解

先给个示例&#xff0c;展示Java 8流API的优势 假设我们有以下任务&#xff1a; 给定一个字符串列表&#xff0c;我们需要执行以下操作&#xff1a; 筛选出所有以"A"开头的字符串。 将这些字符串转换为大写。 对这些字符串按照长度进行排序。 最后&#xff0c;将…...

通过 xlsx 解析上传excel的数据

一、前言 在前端开发中&#xff0c;特别是在后台管理系统中&#xff0c;导入数据&#xff08;上传excel&#xff09;到后端是是否常见的功能&#xff1b;而一般的实现方式都是通过接口将excel上传到后端&#xff0c;再有后端进行数据解析并做后续操作。 今天&#xff0c;来记录…...

Flink系列之:JDBC SQL 连接器

Flink系列之&#xff1a;JDBC SQL 连接器 一、JDBC SQL 连接器二、依赖三、创建 JDBC 表四、连接器参数五、键处理六、分区扫描七、Lookup Cache八、幂等写入九、JDBC Catalog十、JDBC Catalog 的使用十一、JDBC Catalog for PostgreSQL十二、JDBC Catalog for MySQL十三、数据…...

OpenCV与YOLO学习与研究指南

引言 OpenCV是一个开源的计算机视觉和机器学习软件库&#xff0c;而YOLO&#xff08;You Only Look Once&#xff09;是一个流行的实时对象检测系统。对于大学生和初学者而言&#xff0c;掌握这两项技术将大大提升他们在图像处理和机器视觉领域的能力。 基础知识储备 在深入…...

hive中map相关函数总结

目录 hive官方函数解释示例实战 hive官方函数解释 hive官网函数大全地址&#xff1a; hive官网函数大全地址 Return TypeNameDescriptionmapmap(key1, value1, key2, value2, …)Creates a map with the given key/value pairs.arraymap_values(Map<K.V>)Returns an un…...

HttpServletRequestWrapper、HttpServletResponseWrapper结合 过滤器 实现接口的加解密、国际化

目录 一、HttpServletRequestWrapper代码 二、HttpServletRequestWrapper代码 三、加解密过滤器代码 四、国际化过滤器代码 一、HttpServletRequestWrapper代码 package com.vteam.uap.security.httpWrapper;import jakarta.servlet.ReadListener; import jakarta.servlet.…...

最大通关数

洛洛和晶晶计划一起挑战峡谷深渊&#xff0c;峡谷左右有不同数量的关卡&#xff0c;每个关卡需要不同的紫水晶通关&#xff0c;用给定的紫水晶依次通过最多的关卡。 (笔记模板由python脚本于2023年12月23日 12:16:50创建&#xff0c;本篇笔记适合熟悉贪心算法的coder翻阅) 【学…...

MySQL中EXPLAIN关键字解释

什么是MySQL的索引 索引是帮助MySQL高效获取数据的数据结构 MySQL再存储数据之外&#xff0c;数据库系统中还维护者满足特定查找算法的数据结构&#xff0c;这些数据结构以某种引用表中的数据&#xff0c;这样我们就可以通过数据结构上实现的高级查找算法来快速…...

初始JavaScript详解【精选】

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍初始JavaScript以及部分理论知识 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主收将持续更新学习记录获&#xff0c;友友们有任何问题可以在评论区留言 目录 ⭐…...

计数排序,基数排序及排序总结

稳定性&#xff1a;当要排序的数组有相同数据时&#xff0c;排序后相同数据的相对位置不变&#xff0c;则称该排序算法稳定&#xff0c;否则即为不稳定. 在这里我在说说计数排序吧&#xff0c;计数排序就是将给定数组中的数进行计数&#xff0c;在从小到大依次输出即可。简单过…...

【LeetCode】459. 重复的子字符串(KMP2.0)

今日学习的文章链接和视频链接 leetcode题目地址&#xff1a;459. 重复的子字符串 代码随想录题解地址&#xff1a;代码随想录 题目简介 给定一个非空的字符串 s &#xff0c;检查是否可以通过由它的一个子串重复多次构成。 看到题目的第一想法(可以贴代码&#xff09; 1.…...

CSS(五) -- 动效实现(立体盒子旋转-四方体+正六边)

一. 四面立体旋转 正方形旋转 小程序中 wxss中 <!-- 背景 --><view class"dragon"><!--旋转物体位置--><view class"dragon-position"><!--旋转 加透视 有立体的感觉--><view class"d-parent"><view …...

Win10使用OpenSSL生成证书的详细步骤(NodeJS Https服务器源码)

远程开启硬件权限&#xff0c;会用到SSL证书。 以下是Win10系统下用OpenSSL生成测试用证书的步骤。 Step 1. 下载OpenSSL,一般选择64位的MSI Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions 一路点下来&#xff0c;如果后续请你捐款&#xff…...

sql_lab之sqli中的堆叠型注入(less-38)

堆叠注入&#xff08;less-38&#xff09; 1.判断注入类型 http://127.0.0.3/less-38/?id1 and 12 -- s 没有回显 http://127.0.0.3/less-38/?id1 and 11 -- s 有回显 则说明是单字节’注入 2.查询字段数 http://127.0.0.3/less-38/?id1 order by 4 -- s 报错 http:/…...

网站做子页面怎么做的/搜索引擎营销方法有哪些

在下笔写SQL系列文章时&#xff0c;我突然有点懵&#xff0c;因为从某种意义上来说SQL是我熟悉的陌生人。熟悉是因为我和SQL很早就已相遇&#xff0c;回首整个过程&#xff0c;我们经历过浅浅的相知&#xff0c;长长的相忘于江湖&#xff0c;紧接着又是短暂的重逢&#xff0c;然…...

asp网站域名/seo网站推广简历

由MySQL提供的模式匹配的其它类型是使用扩展正则表达式。当你对这类模式进行匹配测试时&#xff0c;使用REGEXP和NOT REGEXP操作符(或RLIKE和NOT RLIKE&#xff0c;它们是同义词)。3. 正则表达式的使用&#xff1b;扩展正则表达式的一些字符是&#xff1a; ‘.’匹配任…...

安装wordpress之后/谷歌搜索引擎下载

前提 这里做下笔记&#xff0c;记录下项目中遇到的问题 eslint别名设置 javascrpt开发 没有使用typescript之前&#xff0c;只需要在webpack或者vue.config.js设置alias即可。 // WEBPACK配置 configureWebpack{resolve: {extensions: [.js, .less, .sass, .css, .vue],alias:…...

wordpress中动态设置轮播图片/链接购买

&#xfeff;&#xfeff;Floating Point Exception Floating Point Exception就是浮点数异常&#xff0c;常常发生在编译好的程序&#xff0c;放入到另外台机器上运行时 。产生浮点数异常的原因是两台机器版本不同&#xff0c;程序在高版本的系统编译好&#xff0c;放入低版本…...

给宝宝做衣服网站好/微信推广

startsWith()方法 startsWith()方法用来判断当前字符串是否是以另外一个给定的子字符串“开头”的&#xff0c;根据判断结果返回 true 或 false 参数: str.startsWith(searchString [, position]) searchString 要搜索的子字符串 position 在 str 中搜索 searchString 的…...

品牌网站设计案例/怎样把自己的产品放到网上销售

扩展就是向一个已有的类、结构体或枚举类型添加新功能&#xff08;functionality&#xff09;。这包括在没有权限获取原始源代码的情况下扩展类型的能力&#xff08;即逆向建模&#xff09;。扩展和 Objective-C 中的分类&#xff08;categories&#xff09;类似。&#xff08;…...