Day10 树---哈夫曼树 家谱处理 搜索树判断 目录树

Posted 回忆过去,是最美好的事情。

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day10 树---哈夫曼树 家谱处理 搜索树判断 目录树相关的知识,希望对你有一定的参考价值。

Huffman Codes

本题的思路是先创建哈夫曼树,求得最优WPL。之后先将待测作业的WPL计算得出,若不同于哈夫曼树的WPL必不正确;之后将待测作业编码建树来判断是否前缀冲突,若前缀冲突必不正确,反之正确。
检测编码是否冲突的核心

  • 首先将所有作业中的编码按照长度递减排序
  • 之后将编码建树,编码的最后一位应该落在空处;如果没有落在空处说明编码冲突。
#include<bits/stdc++.h>
#include<map>
#include<string>
#include<queue>
using namespace std;
struct Node{
	char val;
	int weight;
	Node*left=nullptr;
	Node*right=nullptr;
	Node(){
	}
	Node(char val,int weight):val(val),weight(weight){
	}
	Node(char val,int weight,Node*left,Node*right):val(val),weight(weight),left(left),right(right){
	}
};
//类的小根堆重载 
struct cmp{
	bool operator()(Node*a,Node*b){
		return a->weight>b->weight;
	}
};
//创建哈夫曼树 
Node* buildHuffmanTree(priority_queue<Node*,vector<Node*>,cmp>pqueue){
	while(pqueue.size()>1){
		Node*left=pqueue.top();pqueue.pop();
		Node*right=pqueue.top();pqueue.pop();
		pqueue.push(new Node(' ',left->weight+right->weight,left,right));
	}
	return pqueue.top();
}
int N,M;
//存储字母及频率 
map<char,int>Map;
//计算哈夫曼树的WPL 
void getWPL(Node*root,int&WPL,int step){
	if(root==nullptr)
		return;
	if(root->left==nullptr&&root->right==nullptr){
		WPL+=Map[root->val]*step;
	}
	getWPL(root->left,WPL,step+1);
	getWPL(root->right,WPL,step+1);
}
//判断第二步建树是否出现问题 
bool flag=true;
//建树检查编码是否冲突 
Node* build(Node*root,string&code,int pos){
	if(pos>=code.size()){
		return root;
	}
	if(pos==code.size()-1){
		if((code[pos]=='0'&&root->left!=nullptr)||(code[pos]=='1'&&root->right!=nullptr)){
			flag=false;
			return nullptr;
		}
	}
	if(code[pos]=='0'){
		if(root->left==nullptr){
			root->left=new Node();
		}
		root->left=build(root->left,code,pos+1);
	}else{
		if(root->right==nullptr){
			root->right=new Node();
		}
		root->right=build(root->right,code,pos+1);
	}
	return root;
}

int main(){
	cin>>N;
	char c;int freq;
	priority_queue<Node*,vector<Node*>,cmp>pqueue;
	for(int i=0;i<N;++i){
		cin>>c>>freq;
		Map[c]=freq;
		pqueue.push(new Node(c,freq));
	}
	Node*root=buildHuffmanTree(pqueue);
	int WPL=0;
	getWPL(root,WPL,0);
	cin>>M;
	while(M--){
		string code;
		int cost=0;
		vector<string>testCode;
		flag=true; 
		for(int i=0;i<N;++i){
			cin>>c>>code;
			cost+=Map[c]*code.size();
			testCode.push_back(code); 
		}
		if(cost!=WPL){
			cout<<"No"<<endl;
		}else{
			Node*root=new Node();
			sort(testCode.begin(),testCode.end(),[](string a,string b){
				return a.size()>b.size();
			});
			for(string code:testCode){
				root=build(root,code,0);
				if(!flag){
					cout<<"No"<<endl;
					break;
				}
			}
			if(flag){
				cout<<"Yes"<<endl;
			}
		}
	}
	return 0;
}

家谱处理

使用寻找父亲结点,使用map连接虚关系<子节点,父节点>

#include<bits/stdc++.h> 
#include<map>
#include<stack>
using namespace std;
//子节点-->父节点 
map<string,string>Map;
//结点名称 缩进 
stack<pair<string,int> >S;
int N,M; 
int main(){
	cin>>N>>M;
	getchar();
	string str;
	string name;
	while(N--){
		getline(cin,str);
		size_t pos=str.find_first_not_of(" ");
		name=str.substr(pos);
		if(S.empty()){
			Map[name]="null";
			S.push(make_pair(name,0));
		}else{
			while(!S.empty()&&S.top().second>=pos){
				S.pop();
			}
			Map[name]=S.top().first;
			S.push(make_pair(name,pos));
		}
	}
	while(M--){
		string x,relation,y,temp;
		cin>>x>>temp>>temp>>relation>>temp>>y;
		if(relation=="child"||relation=="parent"){
			if(relation=="parent")
				swap(x,y);
			if(Map[x]==y)
				cout<<"True"<<endl;
			else
				cout<<"False"<<endl;
		}else if(relation=="ancestor"||relation=="descendant"){
			if(relation=="descendant")
				swap(x,y);
			bool flag=false;
			while(Map[y]!="null"){
				if(x==Map[y]){
					flag=true;
					break;
				}
				y=Map[y];
			}
			if(flag)
				cout<<"True"<<endl;
			else
				cout<<"False"<<endl;
		}else{
			if(Map[x]==Map[y])
				cout<<"True"<<endl;
			else
				cout<<"False"<<endl;
		}
	}
	return 0;
}

搜索树判断

本题给出二叉搜索树或者二叉镜像树的前序遍历,判断是否正确,并且给出后序遍历。

最初想的是将前序遍历排序得到中序遍历,利用前序遍历和中序遍历就可建树,不过过于麻烦,首先判断是二叉搜索树还是镜像搜索树,之后在建树的过程中还需根据树的类型查找中序遍历中根的位置。二叉搜索树的根在中序遍历为第一个大于等于根的值的位置;而二叉镜像树的根在最后一个大于等于根的值的位置。 不过最终没有满分。
现在该思路为利用先序遍历作为输入,建立二叉搜索树,求得前序遍历。二叉镜像树的前序遍历可由二叉搜索树得到,只需在前序遍历时先访问右子结点。相同方法得到后序遍历。判断输入遍历是否为二叉搜索树或二叉镜像树遍历的一种,之后输出后序遍历即可。

#include<bits/stdc++.h>
#include<vector>
using namespace std;
vector<int>originalPre;
vector<int>pre,preMirror,post,postMirror;
struct Node {
	int val;
	Node*left=nullptr;
	Node*right=nullptr;
	Node() {
	}
	Node(int val):val(val) {
	}
	Node(int val,Node*left,Node*right):val(val),left(left),right(right) {
	}
};
Node* insert(Node*root,int newVal) {
	if(root==nullptr) {
		root=new Node(newVal);
	} else if(newVal<root->val) {
		root->left=insert(root->left,newVal);
	} else
		root->right=insert(root->right,newVal);
	return root;
}
void preOrder(Node*root) {
	if(root==nullptr) {
		return;
	}
	pre.push_back(root->val);
	preOrder(root->left);
	preOrder(root->right);
}
void preMirrorOrder(Node*root){
	if(root==nullptr){
		return;
	}
	preMirror.push_back(root->val);
	preMirrorOrder(root->right);
	preMirrorOrder(root->left);
}
void postOrder(Node*root){
	if(root==nullptr){
		return;
	}
	postOrder(root->left);
	postOrder(root->right);
	post.push_back(root->val);
}
void postMirrorOrder(Node*root){
	if(root==nullptr){
		return;
	}
	postMirrorOrder(root->right);
	postMirrorOrder(root->left);
	postMirror.push_back(root->val);
}
int main() {
	int N;
	int val;
	Node*root=nullptr;
	cin>>N;
	for(int i=0; i<N; ++i) {
		cin>>val;
		originalPre.push_back(val);
		root=insert(root,val);
	}
	preOrder(root);
	preMirrorOrder(root);
	postOrder(root);
	postMirrorOrder(root);
	//二叉搜索树的先序遍历 
	if(originalPre==pre){
		cout<<"YES"<<endl;
		cout<<post[0];
		for(int i=1;i<post.size();++i){
			cout<<" "<<post[i];
		} 
		cout<<endl;
	}else if(originalPre==preMirror){
		cout<<"YES"<<endl;
		cout<<postMirror[0];
		for(int i=1;i<postMirror.size();++i){
			cout<<" "<<postMirror[i];
		}
		cout<<endl;
	}else{
		cout<<"NO"<<endl;
	}
	return 0;
}

目录树

根据目录归档文件重建目录树,需要注意的是目录需要在文件之前打印,需要在Node中设置一个表示isFolder来记录。递归建树,递归打印。

#include<bits/stdc++.h>
#include<map>
using namespace std;
struct Node {
	//结点名称 
	string name;
	//子节点名称,地址,递归使用
	map<string,Node*>subFiles;
	//判断该节点是否为文件夹 
	bool isFolder;
	Node() {
	}
	Node(string name):name(name) {
	}
	Node(string name,bool isFolder):name(name),isFolder(isFolder){}
};
//加文件 
Node* add(Node*root,string file) {
	if(file.find("\\\\")==string::npos) { //文件
		if(root->subFiles.find(file)==root->subFiles.end())//文件已存在不需重新创建 
			root->subFiles[file]=new Node(file,false);
	} else if(file.find("\\\\")==file.size()-1) { //目录文件
		if(root->subFiles.find(file.substr(0,file.size()-1))==root->subFiles.end())//目录已存在,不需重新创建 
			root->subFiles[file.substr(0,file.size()-1)]=new Node(file.substr(0,file.size()-1),true);
	} else { //多级目录
		size_t pos=file.find_first_of("\\\\");
		string firstDir=file.substr(0,pos);
		//目录不存在直接创建
		if(root->subFiles.find(firstDir)==root->subFiles.end())
			root->subFiles[firstDir]=new Node(firstDir,true);
		root->subFiles[firstDir]=add(root->subFiles[firstDir],file.substr(pos+1));
	}
	return root;
}
void dfs(Node*root,int depth) {
	if(root==nullptr)
		return;
	for(int i=0; i<depth; ++i)
		cout<<" ";
	cout<<root->name<<endl;
	vector<Node*>subFiles;
	for(auto it:root->subFiles) {
		subFiles.push_back(it.second);
	}
	sort(subFiles.begin(),subFiles.end(),[](Node*a,Node*b) {
		//同为文件或者目录 
		if(a->isFolder&&b->isFolder||!a->isFolder&&!b->isFolder){
			return a->name<b->name; 
		}else
			return a->isFolder;
	});
	for(Node*subFile:subFiles) {
		dfs(subFile,depth+2);
	}
}
int main() {
	Node*root=new Node("root",true);
	int N;
	cin>>N;
	string file;
	for(int i=0; i<N; ++i) {
		cin>>file;
		root=add(root,file);
	}
	dfs(root,0);
	return 0;
}

以上是关于Day10 树---哈夫曼树 家谱处理 搜索树判断 目录树的主要内容,如果未能解决你的问题,请参考以下文章

Day10 树---哈夫曼树 家谱处理 搜索树判断 目录树

Day10 树---哈夫曼树 家谱处理 搜索树判断 目录树

数据结构树与树的表示二叉树存储结构及其遍历二叉搜索树平衡二叉树堆哈夫曼树与哈夫曼编码集合及其运算

第04次作业-树

树问题总结之哈夫曼树

[转]哈夫曼树