哈夫曼树/优先队列/可重集合/堆

哈夫曼树

前置c语言知识

结构体指针

// typedef struct Student*  Stu;
//这里相当于给结构体指针变量起名为Stu,注意在这里Stu为一个类型,而不是变量,Stu st1,st2要访问结构体中的数据时st1->成员变量;
//结构体类型定义: 
struct Student{
	      char number;
	      char name;
	      bool sex;
	      struct Student *Next;//结构体支持自定义
};
typedef struct Student *Stu;
(也可以放在结构体前,但这种写法不太规范)
Stu st1;//st1定义为struct Student 类型的指针变量

上面的例子等价于:
typedef struct Student{
          char number;
	      char name;
	      bool sex;
	      struct Student *Next;
}*Stu;
Stu st1;//这个语句定义了一个名为st1的结构体指针

优先队列

实现霍夫曼树需要用堆的功能,但是我们不重复写轮子,这里我们直接使用STL优先队列实现。要注意优先队列/堆里面存储的是结构体指针node *,而不是结构体节点node。否则无法建树,所以使用结构体方法重载小于号。(第三种)
四种重载优先级的方法。霍夫曼树实现的难点就在这里

  1. 结构体内部重载小于号
priority_queue <node> q;
//node是一个结构体
//结构体里重载了‘<’小于符号

  1. 使用标准库函数写法

priority_queue <int,vector<int>,greater<int> > q;
//不需要#include<vector>头文件
//注意后面两个“>”不要写在一起,“>>”是右移运算符
priority_queue <int,vector<int>,less<int> >q;
  1. 结构体外自定义重载方法,注意此处是结构体而非函数
//优先队列的重载大小方法是一个结构体
struct cmp
{
	bool operator () (const node &x,const node &y) const
	{
		return //需要的真值情况;
	}
};

priority_queue<node,vector<node>,cmp> q;
  1. 使用decltype关键字进行重载

这个是从网上看到的不太明白原理。

auto f = [](Tnode* t1, Tnode* t2) {return t1->weight > t2->weight; };
priority_queue<Tnode*, vector<Tnode*>, decltype(f)>heap(f);
优先队列练习

美团笔试题
餐厅n个桌子,男喜欢坐一个人桌子,女喜欢坐没有人桌子,除非没符合条件的。
给出男女进入顺序,安排编号最小座位。

#include<bits/stdc++.h>
#include<queue>
#include<bits/stdc++.h>

using namespace std;

priority_queue<int,vector<int>, greater<int>  >number_0,number_1,init;
const int N=5e5+10;
char s[N],sex[2*N];//1e7级别的输出千万别用cin,关闭cin流动也过不了 

void add_0(){
	int k=number_0.top();
	number_0.pop();
	number_1.push(k);
	printf("%d\n",k);
}

void add_1(){
	printf("%d\n",number_1.top());
	number_1.pop();	
}


int main(){
	int t,n,len;
	scanf("%d",&t);
	while(t--){
		number_0=init;
		number_1=init;
		//cin>>n>>s;
		scanf("%d",&n);
		scanf("%s",s);
		for(int i=0;i<n;i++){
			if(s[i]=='0') number_0.push(i+1);
			if(s[i]=='1') number_1.push(i+1);
		}
		
		
		scanf("%d",&len);
		scanf("%s",sex);
		for(int i=0;i<len;i++){
			if(sex[i]=='M'){
				if(!number_1.empty())add_1();
				else add_0();
			}
			if(sex[i]=='F'){
				if(!number_0.empty())add_0();
				else add_1();
			}			
		}	
	}
	
} 

可重集合

每次把一块蛋糕对半分,问是否可以分出n块大小为ai的蛋糕。用mutiset模拟即可。

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
     
    int N;
    ll S;
    int A[202020];
     
    int main() {
    	int tc; scanf("%d", &tc);
    	while(tc--) {
    		multiset<ll> X, Y;
    		scanf("%d", &N); S = 0;
    		for(int i = 1; i <= N; i++) {
    			scanf("%d", &A[i]);
    			S += A[i];
    			X.insert(A[i]);//子节点 
    		}
    		if(N == 1) { puts("YES"); continue; }
    		Y.insert(S);//和 
    		while(Y.size()) {
    			if(X.size() < Y.size()) break;//x节点数更少就退出 
    			auto it = prev(Y.end());// 取得最大值 
    			ll t = *it;
    			Y.erase(it);//删除it,将当前值拆分成两部分 
    			ll a = t / 2, b = (t + 1) / 2;
    			if(X.find(a) != X.end()) X.erase(X.find(a)); //X中有a就删除a 
    			else Y.insert(a);
    			if(X.find(b) != X.end()) X.erase(X.find(b));
    			else Y.insert(b);
//multiset::find(是C++ STL中的内置函数,该函数返回指向在多集容器中搜索的元素的lower_bound的迭代器。
//如果未找到该元素,则迭代器指向该集合中最后一个元素之后的位置
    		}
    		if(Y.size()) puts("NO");
    		else puts("YES");
    	}
    	return 0;
    }
//	new_iterator = prev(iterator,n)	
//	当“n“为正数时,返回传入迭代器“iterator”左边,距离”iterator“ n个单位的迭代器”new_iterator“。
//	当“n“为负数时,返回传入迭代器“iterator”右边,距离”iterator“ n个单位的迭代器"new_iterator"。
//	new_iterator = prev(iterator)
//	不写n的话,默认向“iterator”左边移动1个单位。
//	如果是随机访问迭代器,就只执行一次运算符操作 +=n( -=n ),否则,执行n次持续的递减或递增操作 ++(--)。

哈夫曼树建树

知乎代码模板
例题练习

这个模板同样可以用于求前缀编码

#include<bits/stdc++.h>

using namespace std;

struct Tnode
{
	int weight;
	Tnode* left, * right;
	Tnode(int x) :weight(x), left(nullptr), right(nullptr) {};
};

struct cmp{
	bool operator()( Tnode* x, Tnode* y)  {
		return x->weight > y->weight;//值小优先 
	}
};

pair<Tnode*, int> Huffman(const vector<int>& v)
{
	//auto f = [](Tnode* t1, Tnode* t2) {return t1->weight > t2->weight; };
	//priority_queue<Tnode*, vector<Tnode*>, decltype(f)>heap(f);
	priority_queue<Tnode*, vector<Tnode*>, cmp>heap;
	for (auto& x : v) heap.push(new Tnode(x));
	//Tnode* p=new Tnode(x) 使用结构体指针指像实例化节点
	int sum = 0;
	while (heap.size() > 1)
	{
		auto t1 = heap.top(); heap.pop();
		auto t2 = heap.top(); heap.pop();
		auto r = new Tnode(t1->weight + t2->weight);
		r->left = t1; r->right = t2;
		sum+= (t1->weight + t2->weight);
		heap.push(r);
	}
	return { heap.top(),sum };
}

int dfs(Tnode* root, int pathlen)                         //根据Huffman树根节点求出带权路径长度之和
{
	if (!root->left && !root->right) return pathlen * root->weight;
	return dfs(root->left, pathlen + 1) + dfs(root->right, pathlen + 1);
}

int main(){
	int n;
	cin>>n;
	vector<int >a;
	while(n--){
		int val;
		cin>>val;
		a.push_back(val);
	}
	cout<<Huffman(a).second<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值