第19期:树和二叉树

1.二叉树的编号

1.1 UVA679 小球下落 Dropping Balls

#include <cstdio>
#include <iostream>
using namespace std;

//快读
inline int read() {
    int x = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = x * 10 + ch -'0', ch = getchar();
    return x;
}

int main() {
    int T = read();
    while (T--) {
        int D = read(), I = read(), ans = 1; //ans是小球当前所在的节点
        //要下落D-1次
        for (int i = 0; i < D - 1; ++i) {
            //奇数个就落到左子树上
            if (I % 2) ans = ans * 2; //小球是第几个落在当前节点的
            //偶数个就落到右子树上
            else ans = ans * 2 + 1;
            I = (I + 1) / 2;
        }
        printf("%d\n", ans); 
    }
    return 0;
}

2.二叉树的层次遍历

2.1 UVA122 树的层次遍历 Trees on the level

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+100;
char s[maxn];//保存读入结点
bool failed;
//结点类型
struct Node{
	bool have_value;//是否被赋值过
	int v;//结点值
	Node *left,*right;
	Node():have_value(false),left(NULL),right(NULL){}//构造函数 
};
Node* root;//二叉树的根结点
Node* newnode(){ return new Node(); }
inline void remove_tree(Node* u){
	if(u==NULL) return;//提前判断比较稳妥
	remove_tree(u->left);//递归释放左子树的空间
	remove_tree(u->right);//递归释放右子树的空间
	delete u;//调用u的析构函数并释放u结点本身的内存 
} 
//用队列实现bfs完成层次顺序遍历这棵树
inline bool bfs(vector<int>& ans){
	queue<Node*> q;
	ans.clear();
	q.push(root);//初始时只有一个根结点
	while(!q.empty()){
		Node* u=q.front(); q.pop();
		if(!u->have_value) return false;//有结点没有被赋值过,表明输入有误
		ans.push_back(u->v);//增加到输出序列尾部
		if(u->left!=NULL) q.push(u->left);//把左子结点(如果有)放进队列 
		if(u->right!=NULL) q.push(u->right);//把右子结点(如果有)放进队列 
	} 
	return true;//输入正确 
}   
inline void addnode(int v,char* s){
	int n=strlen(s);
	Node* u=root; //从根结点开始往下走
	for(int i=0;i<n;i++){
		if(s[i]=='L'){
			if(u->left==NULL) u->left=newnode();//结点不存在,建立新结点
			u=u->left;//往左走 
		}else if(s[i]=='R'){
			if(u->right==NULL) u->right=newnode();
			u=u->right;//往右走 
		} //忽略其他情况,即最后那个多余的右括号 
	} 
	if(u->have_value) failed=true;//已经赋过值,表明输入有误
	u->v=v;
	u->have_value=true;//别忘记做标记 
} 
inline bool read_input(){
	failed=false;
	remove_tree(root);
	root=newnode();//创建根结点
	for(;;){
		if(scanf("%s",s)!=1) return false;//整个输入结束
		if(!strcmp(s,"()")) break;//读到结束标志,退出循环
		int v;
		sscanf(&s[1],"%d",&v);//读入结点值
		addnode(v,strchr(s,',')+1);//查找逗号,然后插入结点 
	} 
	return true; 
}
vector<int> ans;
int main(){
	while(read_input()){
		if(failed || !bfs(ans)) {
            printf("not complete");
        }else {
            for(vector<int>::iterator t=ans.begin(); t!=ans.end(); t++) {
                if(t != ans.end()-1) {
                    printf("%d ",*t);
                }else {
                    printf("%d",*t);
                }
            }
        }
        printf("\n");
	}	
	return 0;
} 

2.2 UVA548 树 Tree

分析:后序遍历的第一个字符就是根,因此只需在中序遍历中找到它,就知道左右子树的中序遍历和后序遍历。这样可以先把二叉树构造出来,然后再执行一次递归遍历,找到最优解。

#include<bits/stdc++.h>
using namespace std;
//因为各个结点的权值各不相同且都是正整数,直接用权值作为结点编号
const int maxv=10000+10;
const int inf=0x7fffffff;
int in_order[maxv],post_order[maxv],lch[maxv],rch[maxv];
int n;

bool read_list(int* a){
	string line;
	if(!getline(cin,line)) return false;
	stringstream ss(line);
	n=0;
	int x;
	while(ss>>x) a[n++]=x;
	return n>0;
} 

//把in_order[L1..R1]和post_order[L2..R2]建成一棵二叉树返回树根
int build(int L1,int R1,int L2,int R2){
	if(L1>R1) return 0;//空树
	int root=post_order[R2];
	int p=L1;
	while(in_order[p]!=root) p++;
	int cnt=p-L1;//左子树的结点个数
	lch[root]=build(L1,p-1,L2,L2+cnt-1);
	rch[root]=build(p+1,R1,L2+cnt,R2-1);
	return root; 
} 

int best,best_sum;//目前为止的最优解和对应的权值和

void dfs(int u,int sum){
	sum+=u;
	if(!lch[u]&&!rch[u]){//叶子
		if(sum<best_sum || (sum==best_sum&&u<best)){
			best=u;
			best_sum=sum;
		} 
	}
	if(lch[u]) dfs(lch[u],sum);
	if(rch[u]) dfs(rch[u],sum); 
} 

int main(){
	while(read_list(in_order)){
		read_list(post_order);
		build(0,n-1,0,n-1);
		best_sum=inf;
		dfs(post_order[n-1],0);
		cout<<best<<"\n";
	}
	return 0;
}

2.3 UVA839 天平 Not so Mobile

#include<bits/stdc++.h>
using namespace std;
//输入一个子天平,返回天平是否平衡,参数W修改为子天平的总重量。
bool solve(int& W){
	int W1,D1,W2,D2;
	bool b1=true,b2=true;
	cin>>W1>>D1>>W2>>D2;
	if(!W1) b1=solve(W1);
	if(!W2) b2=solve(W2);
	W=W1+W2;
	return b1 && b2 && (W1*D1 == W2*D2);
}
int main(){
	int T,W;
	cin>>T;
	while(T--){
		if(solve(W)) cout<<"YES\n";
		else cout<<"NO\n";
		if(T) cout<<"\n";
		//两组询问输出之间要空一行,最后一组询问不能多输出空行
	}
	return 0;
} 

2.4 UVA699 下落的树叶 The Falling Leaves

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+100;
int sum[maxn];
//输入并共计一颗子树,树根水平位置为p
void build(int p){
	int v; cin>>v;
	if(v==-1) return;//空树
	sum[p]+=v;
	build(p-1);build(p+1); 
} 

//边读入边统计
bool init(){
	int v; cin>>v;
	if(v==-1) return false;
	memset(sum,0,sizeof(sum));
	int pos=maxn/2;//树根的水平位置
	sum[pos]=v;
	build(pos-1); build(pos+1); 
	return true;
} 

int main(){
	int kase=0;
	while(init()){
		int p=0;
		while(sum[p]==0) p++;//找最左边的叶子
		cout<<"Case "<<++kase<<":\n"<<sum[p++];//因为要避免行末多余空格
		while(sum[p]!=0) cout<<" "<<sum[p++];
		cout<<"\n\n"; 
	}
	return 0;
}

2.5 UVA297 四分树 Quadtrees

#include <bits/stdc++.h>
using namespace std ;

const int len = 32 ;
const int maxn = 32 * 32 + 10 ;//字符串最大不超过1024

char s[maxn] ;
int buf[len][len] ;
int cnt ;

//把字符串s[p..]导出到以(r,c)为左上角,边长为w的缓冲区中
//2 1
//3 4
void draw(const char* s, int& p, int r, int c, int w){
	char ch = s[p] ;
	p ++ ;
	if (ch == 'p'){//Both black and white
		int nxt = w / 2 ;
		draw(s, p, r      , c + nxt, nxt) ;//1
		draw(s, p, r      , c      , nxt) ;//2
		draw(s, p, r + nxt, c      , nxt) ;//3
		draw(s, p, r + nxt, c + nxt, nxt) ;//4
		return ;
	}else if (ch == 'f'){//画黑像素(白像素不画) 
		for (int i = r ;i < r + w ;i ++){
			for (int j = c; j < c + w; j ++){
				if (buf[i][j]==0){
					buf[i][j] = 1 ;
					cnt ++ ;
					// count the black pixels
				}
			}
		}
	}
	return ;
}

int main(){
	int T ;
	scanf("%d", &T) ;
	while(T --){
		cnt = 0 ;
		memset(buf, 0, sizeof(buf)) ;
		for (int i = 1 ;i <= 2 ;i ++){
			scanf("%s", &s) ;
			int p = 0 ;
			draw(s, p, 0, 0, len) ;
		}
		printf("There are %d black pixels.\n", cnt) ;
	}
	return 0 ;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值