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]++;}}
}

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

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

相关文章

【3D生成与重建】SSDNeRF:单阶段Diffusion NeRF的三维生成和重建

系列文章目录 题目&#xff1a;Single-Stage Diffusion NeRF: A Unified Approach to 3D Generation and Reconstruction 论文&#xff1a;https://arxiv.org/pdf/2304.06714.pdf 任务&#xff1a;无条件3D生成&#xff08;如从噪音中&#xff0c;生成不同的车等&#xff09;、…

Proxmox VE 超融合集群不停服务新增配置(磁盘)

五个节点的Proxmox VE超融合集群&#xff0c;创建了两个Ceph Pool,一个是高速的Nvme存储池&#xff0c;另一个是大容量SATA存储池。现在需要将原有的SATA磁盘全部拔出来&#xff0c;换成高速Nvme磁盘。 ​ ​ 首先&#xff0c;先销毁由SATA机械盘组成的“hdd_pool”&#xff…

本地使用 docker 运行OpenSearch + Dashboard + IK 分词插件

准备基础镜像 注意一定要拉取和当前 IK 分词插件版本一致的 OpenSearch 镜像: https://github.com/aparo/opensearch-analysis-ik/releases 写这篇文章的时候 IK 最新版本 2.11.0, 而 dockerhub 上 OpenSearch 最新版是 2.11.1 如果版本不匹配的话是不能用的, 小版本号对不上…

探索栈数据结构:深入了解其实用与实现(c语言实现栈)

上次结束了链表部分的内容&#xff1a;链接未来&#xff1a;深入理解链表数据结构&#xff08;二.c语言实现带头双向循环链表&#xff09; 然而&#xff0c;当我们涉及特定问题时&#xff0c;另一个非常有用的数据结构也开始显得至关重要——栈 栈与链表有着截然不同的特性&a…

用友U8CRM系统help2 任意文件读取漏洞复现

用友U8CRM系统的help2文件中接口存在任意文件读取漏洞&#xff0c;攻击者在未登录情况下即可进行漏洞利用。 1.1 漏洞级别 高危 1.2 快速检索 fofa语法&#xff1a; title"用友U8CRM"1.3 漏洞复现 该漏洞利用非常简单&#xff0c;只需构造get请求 访问该地址即可…

STL中优先队列的模拟实现与仿函数的介绍

文章目录 仿函数优先队列的模拟实现 仿函数 上回我们说到&#xff0c;优先队列的实现需要用到仿函数的特性 让我们再回到这里 这里我们发现他传入的用于比较的东西竟然是一个类模板&#xff0c;而不是我们所见到的函数 我们可以先创建一个类&#xff0c;用于比较大小 struc…

使用Velero备份、恢复k8s集群上的资源

一、Velero简介 Velero提供备份和恢复 Kubernetes 集群资源和持久卷的工具。 Velero功能&#xff1a; 对群集进行备份&#xff0c;并在丢失时进行还原。将集群资源迁移到其他集群。 Velero 包括&#xff1a; 在群集上运行的服务器在本地运行的命令行客户端 开源地址&…

【MySQL】MySQL的数据类型

MySQL的数据类型 一、数据类型分类二、数值类型1、整数类型2、bit类型3、小数类型 三、字符串类型四、时间日期类型五、enum和set类型enum和set查找 数据类型的作用&#xff1a; 决定了存储数据时应该开辟的空间大小和数据的取值范围。决定了如何识别一个特定的二进制序列。 …

【自营版】物流系统+取件员收件员/运营级快递系统小程序源码

后端php前端原生小程序 mysql数据库 主要功能&#xff1a; 寄快递 查快递 多门店 市内取送 取件员中心在线接单 提前预约 也可 立即下单 门店入住 取件员入住

需求分析工程师岗位的职责描述(合集)

需求分析工程师岗位的职责描述1 职责&#xff1a; 1&#xff0c;负责需求调研&#xff0c;对需求进行分析&#xff0c;编写解决方案、需求规格说明书等 2&#xff0c;根据需求制作原型&#xff0c;并负责原型展示以及客户沟通等工作 3&#xff0c;负责向技术团队精确地传达业务…

python算法例23 落单的数Ⅰ

1. 问题描述 给出2n1个非负整数元素的数组&#xff0c;除其中一个数字之外&#xff0c;其他每个数字均出现两次&#xff0c;找到这个数字。 2. 问题示例 给出[1&#xff0c;2&#xff0c;2&#xff0c;1&#xff0c;3&#xff0c;4&#xff0c;3]&#xff0c;返回4。 3. 代…

Jenkins 插件管理指南

目录 常用插件 插件安装 已安装插件 installed plugins 常用插件 Docker Plugin&#xff1a; 这个插件让Jenkins能够与Docker容器平台进行集成。它允许在Jenkins构建过程中创建、管理和销毁Docker容器&#xff0c;为需要Docker化的项目提供了极大的便利性。对于需要在容器中…