二叉树和哈夫曼树

Entropy(poj 1521)


题目大意

对字符串分别用ASCII编码(每个字符8b)和哈夫曼编码,输出编码前、后的长度并计算压缩比。

解题思路

本题不要求求出编码,只需计算长度,考虑记录字符出现频次,用优先队列记录最小的两个频次,直接计算长度。

未知的代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	priority_queue<int,vector<int>,greater<int>>q;
	string s;
	while(getline(cin,s)&&s!="END"){
		sort(s.begin(),s.end());
		int cnt=1;
		int len=s.size();
		for(int i=1;i<=len;i++){
			if(s[i]!=s[i-1]){
				q.push(cnt);
				cnt=1;
			}else cnt++;
		}
		int ans=0;
		if(q.size()==1)ans=len;
		while(q.size()>1){
			int a=q.top();
			q.pop();
			int b=q.top();
			q.pop();
			q.push(a+b);
			ans+=(a+b);
		}
		q.pop();
		cout<<len*8<<" "<<ans<<" "<<len*8.0/ans<<endl;
	}
	return 0;
}

Safe Or Unsafe(hdu P2527)


题目大意

对字符串按哈夫曼编码统计总权值即频次,判断是否小于等于给定值。这道题与上一题基本相同。

解题思路

对字符串排序,统计字符串频次,将其作为节点权值,利用优先队列计算总和。

未知的代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	priority_queue<int,vector<int>,greater<int>>q;
	int n,t;
	string s;
	cin>>n;
	while(n--){
		cin>>t>>s;
		sort(s.begin(),s.end());
		int cnt=1;
		int len=s.size();
		for(int i=1;i<=len;i++){
			if(s[i]!=s[i-1]){
				q.push(cnt);
				cnt=1;
			}else cnt++;
		}
		int ans=0;
		if(q.size()==1)ans=len;		//只有一种字符,长度即为频次
		while(q.size()>1){
			int a=q.top();
			q.pop();
			int b=q.top();
			q.pop();
			q.push(a+b);
			ans+=(a+b);
		}
		q.pop();
		if(ans<=t)puts("yes");
		else puts("no");
	}
	return 0;
}

FBI树(洛谷P1087)


题目大意

根据给出的01串建树,输出相应后续遍历字符序列。

解题思路

依据题目意思,采用先序遍历的方式建树,即根左右,对01串的操作类似于二分。递归函数先对左右建树并输出节点字符,最后输出根节点字符,对应后序遍历。

未知的代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	int n;
	cin>>n;
	string s;
	cin>>s;
	function<void(int,int)>build=[&](int l,int r){
		if(l<r){
			int mid=(l+r)>>1;
			build(l,mid);
			build(mid+1,r);
		}
		int a=0,b=0;
		for(int i=l;i<=r;i++){
			if(s[i]=='0')a++;
			else b++;
		}
		if(a&b)cout<<"F";
		else if(a)cout<<"B";
		else cout<<"I";
	};
	build(0,(1<<n)-1);
	return 0;
}

求先序排列(洛谷P1030)


题目大意

根据二叉树的中序与后续排列求前序排列。

解题思路

中序遍历左根右,后序遍历左右根,依据上述特点,先取后序排列最后一个字符即为根且易知为树的整个树的根,然后在中序排列里查找这个根,并将其分为左右两个子树序列,那么对应的后序排列也可分为两个子树序列,重复上述操作输出所有节点即为先序遍历,按照根左右输出。

未知的代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	string instr,poststr;
	cin>>instr>>poststr;
	function<void(string,string)>build=[&](string instr,string poststr){
		if(instr.size()<=0)return;
		cout<<poststr.back();
		int mid=instr.find(poststr.back());
		build(instr.substr(0,mid),poststr.substr(0,mid));
		build(instr.substr(mid+1),poststr.substr(mid,instr.size()-mid-1));  //注意后序排列
	};
	build(instr,poststr);
	return 0;
}

新二叉树(洛谷P1305)


题目大意

输入二叉树每个节点的根左右字符串,求先序序列。

解题思路

解法1:根据条件建树,并递归输出先序序列。
解法2:根据先序序列性质,根据给出的根左右单节点字符序进行拼接,'*'可以不做处理,输出时判断跳过即可。

建树解法
#include<bits/stdc++.h>
using namespace std;
struct BTree{
	char ch;
	BTree*l,*r;
	BTree(){}
	BTree(char ch):ch(ch),l(nullptr),r(nullptr){}
}tree;
BTree*build(char ch){
	if(ch=='*')return nullptr;
	return new BTree(ch);
}
BTree*find_node(char ch,BTree*t){
	if(t->ch==ch)return t;
	BTree*ans=nullptr;
	if(t->l)ans=find_node(ch,t->l);
	if(ans!=nullptr)return ans;
	if(t->r)ans=find_node(ch,t->r);
	return ans;
}
void prevorder(BTree*t){
	cout<<t->ch;
	if(t->l)prevorder(t->l);
	if(t->r)prevorder(t->r);
}
int main(){
	int n;
	cin>>n;
	char root,l,r;
	cin>>root>>l>>r;
	tree.ch=root;
	tree.l=build(l);
	tree.r=build(r);
	for(int i=1;i<n;i++){
		cin>>root>>l>>r;
		BTree*node=find_node(root,&tree);
		node->l=build(l);
		node->r=build(r);
	}
	prevorder(&tree);
	return 0;
}
搜索解法
#include<bits/stdc++.h>
using namespace std;
int main(){
	int n;
	string s;
	cin>>n>>s;
	for(int i=1;i<n;i++){
		string ret;
		cin>>ret;
		int pos=s.find(ret[0]);
		s.erase(pos,1);
		s.insert(pos,ret);	//直接拼接
	}
	for(auto&it:s){
		if(it!='*')cout<<it;
	}
	return 0;
}

遍历问题(洛谷P1229)


题目大意

给定二叉树前序遍历和后序遍历结果,求中序遍历种数。

解题思路

依据题意可知,当节点只存在一个儿子时,它既可以做左子树又可以做右子树,会在已知前、后序的条件下存在不同的中序遍历结果,那么求有多少个节点只有一个儿子,然后依据乘法原理不断乘2即可。如果该节点有一个子节点,那么它在前序遍历序列中的下一个节点一定是它的子节点,同时在后序遍历序列中的位置一定是在它的前一个节点的位置。

未知的代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	int ans=1;
	string s1,s2;
	cin>>s1>>s2;
	int len=s1.size();
	for(int i=0;i<len-1;i++){
		for(int j=1;j<len;j++){
			if(s1[i]==s2[j]&&s1[i+1]==s2[j-1])ans<<=1;
		}
	}
	cout<<ans<<endl;
	return 0;
}

对称二叉树(洛谷P5018)


题目大意

根据给定信息的二叉树,找出最大对称(在结构和权值上均对称)二叉树的节点数。

解题思路

创建递归函数,递归遍历左右子树,判断是否相等。记录节点数最值。

未知的代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+5;
struct node{
	int l,r,v;
}a[N];
bool dfs(int x,int y){
	if(x==-1&&y==-1)return true;
	if(x==-1||y==-1)return false;
	if(a[x].v!=a[y].v)return false;
	return dfs(a[x].l,a[y].r)&&dfs(a[x].r,a[y].l);
}
int count(int x){ //递归求节点数
	return x==-1?0:count(a[x].l)+count(a[x].r)+1;
}
signed main(){
	int n,ans=0;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i].v;
	for(int i=1;i<=n;i++)cin>>a[i].l>>a[i].r;
	for(int i=1;i<=n;i++){
		if(dfs(i,i))ans=max(ans,count(i));
	}
	cout<<ans;
	return 0;
}

复读(洛谷P5597)


题目大意

解题思路


荷马史诗(洛谷P2168)


题目大意

解题思路

posted @ 2023-12-28 20:56  Lost-in-love  阅读(29)  评论(0)    收藏  举报