pat树问题总结1 根据前中后三种遍历构建树

这篇博客总结了根据前序、中序、后序遍历构建二叉树的问题,包括如何利用这些遍历方式解决层序遍历、确定最小公共祖先(LCA)、输出特定遍历序列等挑战。同时,文章讨论了在不同情况下如何确定二叉树结构,如在二叉搜索树中寻找LCA,以及如何构建蛇形层序遍历。
摘要由CSDN通过智能技术生成

目录

  • 根据前中后三种遍历构建树
  • dfs,bfs
  • 要真正建树的题

(A1120) 给出一棵二叉树的前序遍历和中序遍历,求这棵树的层序遍历。
分析:模板题。

#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<algorithm> 
using namespace std;
vector<int> pre,in,post;
struct node{
	int data;
	node *lchild,*rchild;
};
int n;
node *create(int inl,int inr,int postl,int postr){
	if(postl>postr) return NULL;
	node *root=new node();
	root->data=post[postr];
	int i=inl;
	while(in[i]!=post[postr]) i++;
	root->lchild = create(inl,i-1,postl,postl+(i-inl)-1);
	root->rchild = create(i+1,inr,postl+(i-inl),postr-1);
	return root;
}
int num=0;
void dfs(node *root){
	queue<node*> q;
	q.push(root);
	while(!q.empty()){
		node *now=q.front();
		q.pop();
		printf("%d",now->data);
		num++;
		if(num<n) printf(" ");
		if(now->lchild!=NULL) q.push(now->lchild);
		if(now->rchild!=NULL) q.push(now->rchild);
	}
}
int main(){
	scanf("%d",&n);
	in.resize(n),post.resize(n); //不要忘记 
	for(int i=0;i<n;i++){
		scanf("%d",&post[i]);
	}
	for(int i=0;i<n;i++){
		scanf("%d",&in[i]);
	}
	node* root=create(0,n-1,0,n-1);
	dfs(root);
	return 0;
}

(A1119)给出一棵树的结点个数n,以及它的前序遍历和后序遍历,输出它的中序遍历,如果中序遍历不唯一就输出No,且输出其中一个中序即可,如果中序遍历唯一就输出Yes,并输出它的中序。
分析:给出前序遍历和后序遍历,可以确定唯一的根节点,但是不能确定左子树和右子树。根节点可以确定为前序遍历的第一个结点或后序遍历的最后一个结点,而需要根据后序根节点的前一个结点v在前序中的位置,来划分左右孩子。具体来说,在前序中找到对应位置i,则i之前的结点v的孩子结点,然后划分区间。以i为根节点的子树只有一个孩子(i-prel>1),那么既可能是左孩子也可能是右孩子,所以不唯一,不唯一的话看做左孩子(因为只输出一个就行)。

#include<iostream>
#include<vector>
using namespace std;
vector<int> pre,in,post;
bool unique=true;

void create(int prel,int prer,int postl,int postr){
	if(prel==prer){  //树只有一个结点 
		in.push_back(pre[prel]);
		return;
	}
	if(pre[prel]==post[postr]){
		int i=prel+1;
		while(i<prer && pre[i]!=post[postr-1]) i++;
		if(i-prel>1){
			create(prel+1,i-1,postl,postl+(i-prel-1)-1);
		}
		else{
			unique=false;
		}
		in.push_back(post[postr]);
		create(i,prer,postl+(i-prel-1),postr-1);
	}
}
int main(){
	int n;
	scanf("%d",&n);
	pre.resize(n),post.resize(n);
	for(int i=0;i<n;i++){
		scanf("%d",&pre[i]);
	}
	for(int i=0;i<n;i++){
		scanf("%d",&post[i]);
	}
	create(0,n-1,0,n-1);
	printf("%s\n%d",unique==true ? "Yes":"No",in[0]);
	for(int i=1;i<in.size();i++){
		printf(" %d",in[i]);
	}
	printf("\n");
	return 0;
}

(A1151)在二叉树中给出任意两个结点,找出他们的LCA(最小公共祖先)(给前序和中序遍历)
分析:本题的特别之处
1.lca函数与之前的对比
2.利用map来记录结点在中序序列中的位置,以便于查找该结点存不存在。所以说当需要查找的时候,要想起map。

#include <iostream>
#include <vector>
#include <map>
using namespace std;
map<int, int> pos;
vector<int> in, pre;
void lca(int prel, int prer, int inl,int inr, int a, int b) {
    if (inl > inr) return;
    int root=pre[prel], inroot_pos=pos[root], aIn_pos = pos[a], bIn_pos = pos[b];
    int lchild=inroot_pos-inl;
    if (aIn_pos < inroot_pos && bIn_pos < inroot_pos) //左子树 
        lca(prel+1, prel+lchild, inl,inroot_pos-1,a,b);
    else if ((aIn_pos < inroot_pos && bIn_pos > inroot_pos) || (aIn_pos > inroot_pos && bIn_pos < inroot_pos))
        printf("LCA of %d and %d is %d.\n", a, b, in[inroot_pos]);
    else if (aIn_pos > inroot_pos && bIn_pos > inroot_pos) //右子树 
        lca(prel+lchild+1,prer,inroot_pos+1,inr,a,b);
    else if (aIn_pos == inroot_pos)
            printf("%d is an ancestor of %d.\n", a, b);
    else if (bIn_pos == inroot_pos)
            printf("%d is an ancestor of %d.\n", b, a);
}
int main() {
    int m, n, a, b;
    scanf("%d %d", &m, &n);
    in.resize(n + 1), pre.resize(n + 1);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &in[i]);
        pos[in[i]] = i;//记录对应数字在中序序列的位置 
    }
    for (int i = 1; i <= n; i++) scanf("%d", &pre[i]);
    for (int i = 0; i < m; i++) {
        scanf("%d %d", &a, &b);
        if (pos[a] == 0 && pos[b] == 0)
            printf("ERROR: %d and %d are not found.\n", a, b);
        else if (pos[a] == 0 || pos[b] == 0)
            printf("ERROR: %d is not found.\n", pos[a] == 0 ? a : b);
        else
            lca(1, n, 1, n, a, b);
    }
    return 0;
}

(A1143)在二叉搜索树中给出任意两个结点,找出他们的LCA(最小公共祖先)(给前序)
分析:注意和上题的区别,本题给的是二叉搜索树。还是按上题的方法用map记录。

#include <iostream>
#include <vector>
#include <map>
using namespace std;
map<int, bool> mp;
int main() {
    int m, n, u, v, a;
    scanf("%d %d", &m, &n);
    vector<int> pre(n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &pre[i]);
        mp[pre[i]] = true;
    }
    for (int i = 0; i < m; i++) {
        scanf("%d %d", &u, &v);
        for(int j = 0; j < n; j++) {
            a = pre[j];
            if ((a >= u && a <= v) || (a >= v && a <= u)) break;
        } 
        if (mp[u] == false && mp[v] == false)
            printf("ERROR: %d and %d are not found.\n", u, v);
        else if (mp[u] == false || mp[v] == false)
            printf("ERROR: %d is not found.\n", mp[u] == false ? u : v);
        else if (a == u || a == v)
            printf("%d is an ancestor of %d.\n", a, a == u ? v : u);
        else
            printf("LCA of %d and %d is %d.\n", u, v, a);
    }
    return 0;
}

(A1138)给出前序和中序,输出后序的第一个。
分析:不用建树。在postorder函数后直接输出in[root]就是后序的第一个。注意要使用一个flag限制只输出一个。

#include<iostream>
#include<vector>
using namespace std;
bool flag=false;
vector<int> pre,in;
void postorder(int prel,int prer,int inl,int inr){
	if(inl>inr||flag==true) return;
	int i=inl;
	while(in[i]!=pre[prel]) i++;
	postorder(prel+1,prel+i-inl,inl,i-1);
	postorder(prel+i-inl+1,prer,i+1,inr);
	if(flag==false){
		printf("%d",in[i]);
		flag=true;
	}
}
int main(){
	int n;
	scanf("%d",&n);
	pre.resize(n);
	in.resize(n);
	for(int i=0;i<n;i++) scanf("%d",&pre[i]);
	for(int i=0;i<n;i++) scanf("%d",&in[i]);
	postorder(0,n-1,0,n-1);
	return 0;
}

(A1127)给出中序和后序遍历,输出蛇形层序遍历(奇数层从左到右,偶数层从右到左)
分析:本题需要建树,并用一个二维数组tree[i][j]保存树,tree[i][0]表示i的左孩子,tree[i][1]表示i的右孩子。用result[i][j]保存层序遍历的结果,i表示层数,用奇偶性控制i,双重循环遍历result数组即可得到结果。

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
vector<int> in, post;
vector<vector<int>> result(35);
int n, tree[35][2], root;
struct node {
    int index, depth;
};

//根据中序和后续建的树要保存到二维数组tree[][]中
void dfs(int &index, int inLeft, int inRight, int postLeft, int postRight) {
    if (inLeft > inRight) return;
    index = postRight;
    int i = 0;
    while (in[i] != post[postRight]) i++;
    //tree[i][0]表示tree[i]的左孩子, tree[i][1]表示tree[i]的右孩子
    dfs(tree[index][0], inLeft, i - 1, postLeft, postLeft + (i - inLeft) - 1);
    dfs(tree[index][1], i + 1, inRight, postLeft + (i - inLeft), postRight - 1);
}
void bfs() {
    queue<node> q;
    q.push(node{root, 0});
    while (!q.empty()) {
        node temp = q.front();
        q.pop();
        result[temp.depth].push_back(post[temp.index]);
        if (tree[temp.index][0] != 0)
            q.push(node{tree[temp.index][0], temp.depth + 1});
        if (tree[temp.index][1] != 0)
            q.push(node{tree[temp.index][1], temp.depth + 1});
    }
}
int main() {
    cin >> n;
    in.resize(n + 1), post.resize(n + 1);
    for (int i = 1; i <= n; i++) cin >> in[i];
    for (int i = 1; i <= n; i++) cin >> post[i];
    dfs(root, 1, n, 1, n);
    bfs();
    printf("%d", result[0][0]);
    for (int i = 1; i < 35; i++) {  //层数
        if (i % 2 == 1) {
            for (int j = 0; j < result[i].size(); j++)
                printf(" %d", result[i][j]);
        } else {
            for (int j = result[i].size() - 1; j >= 0; j--)
                printf(" %d", result[i][j]);
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值