L2-006 树的遍历 (25 分)(map,递归,BFS),L2-011 玩转二叉树 (25 分) ,笛卡尔树

L2-006 树的遍历 (25 分)(map,递归)

给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列。这里假设键值都是互不相等的正整数。

输入格式:

输入第一行给出一个正整数N(≤30),是二叉树中结点的个数。第二行给出其后序遍历序列。第三行给出其中序遍历序列。数字间以空格分隔。

输出格式:

在一行中输出该树的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。

输入样例:

7
2 3 1 5 7 6 4
1 2 3 4 5 6 7

输出样例:

4 1 6 3 5 7 2

前置知识,已知二叉树的前序,中序可以求后序,已经后序中序可以求前序,但是给定前序和后序不能确定中序。

洛谷上有一道已知前序后序求所有不同的中序二叉树可能序列个数的题

P1229 遍历问题

  1. 一般这种题都会有一个特殊的条件,树中每个节点的权值互不相同。这是为了方便定位某个节点在中序遍历中的位置。如果没有这个条件,二叉树可能就不唯一了

看了y总的提示,自己写出来了很nice

  1. 如果要输出层序遍历,可以借鉴线段树建树的思想。
  2. 时间复杂度 O ( n ) ,就得用 u n o r d e r e d m a p 了 O(n),就得用unordered_map了 O(n),就得用unorderedmap
#include
#include
#define debug(x) cout<<#x<<":"<<x<<endl
using namespace std;

const int N = 50;

int a[11000],n,cnt;
int in[N],p[N];
map<int,int>mp;
int lson[N],rson[N];
void dfs(int P,int inl,int inr,int pl,int pr){//用类似线段树建树的思想。
    if(inl>inr||pl>pr)return ;
    int root = p[pr];
    int idx = mp[root];
    // idx - pl 是左子树节点的个数。
    dfs(P*2,inl,idx-1,pl,pl+idx-inl-1);
    dfs(P*2+1,idx+1,inr,pl+idx-inl,pr-1);
    cnt++;
    a[P]=root;
}

int main(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>p[i];
    for(int i=1;i<=n;i++){
        cin>>in[i];
        mp[in[i]]=i;
    }
    dfs(1,1,n,1,n);
    // debug(cnt);
    bool flag=0;
    // 我的写法有一个bug,如果这颗树是一条链的话,数组是开不下的,还好PTA数据水。
    for(int i=1;i<=10000;i++){
        if(!a[i])continue;
        if(!flag){
            cout<<a[i];
            flag=1;
        }
        else{
            cout<<" "<<a[i];
        }
    }
}

把代码改改,不传递第一个idx就变成了已知后序中中序求前序的题。

#include
#include
#define debug(x) cout<<#x<<":"<<x<<endl
using namespace std;

const int N = 50;

int a[N],n,cnt;
int in[N],p[N];
map<int,int>mp;
int lson[N],rson[N];
//前序遍历了
int dfs(int inl,int inr,int pl,int pr){
    if(inl>inr||pl>pr)return ;
    int root = p[pr];
    int idx = mp[root];
    a[++cnt]=root;
    debug(root);
    debug(idx);
    // idx - pl 是左子树节点的个数。
    int lson = dfs(inl,idx-1,pl,pl+idx-inl-1);
    int rson = dfs(idx+1,inr,pl+idx-inl,pr-1);
    
    return root;
}

int main(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>p[i];
    for(int i=1;i<=n;i++){
        cin>>in[i];
        mp[in[i]]=i;
    }
    dfs(1,n,1,n);
    bool flag=0;
    for(int i=1;i<=cnt;i++){
        if(!flag){
            cout<<a[i];
            flag=1;
        }
        else{
            cout<<" "<<a[i];
        }
    }
}
y总直接用hash表存每个节点的儿子是谁,这样就可以O(N)了。
#include
#include
#include
#include
#define debug(x) cout<<#x<<":"<<x<<endl
using namespace std;

const int N = 50;
int n;
int in[N],p[N],root;
unordered_map<int,int>mp,lson,rson;

int dfs(int inl,int inr,int pl,int pr){//用类似线段树建树的思想。
    int root = p[pr];
    int idx = mp[root];
    // idx - pl 是左子树节点的个数。
    if(inl<idx)
        lson[root] = dfs(inl,idx-1,pl,pl+idx-inl-1);
    if(idx<inr)
        rson[root] = dfs(idx+1,inr,pl+idx-inl,pr-1);
    return root;
}

void bfs(int S){
    bool flag=1;
    queue<int>que;
    que.push(S);
    while(que.size()){
        auto t = que.front();
        que.pop();
        if(flag){
            cout<<t;
            flag=0;
        }
        else{
            cout<<" "<<t;
        }
        if(lson.count(t)){//不是lson[t].count(),别记错了
            que.push(lson[t]);
        }
        if(rson.count(t)){
            que.push(rson[t]);
        }
    }
}

int main(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>p[i];
    for(int i=1;i<=n;i++){
        cin>>in[i];
        mp[in[i]]=i;
    }
    root = dfs(1,n,1,n);
    bfs(root);
}

L2-011 玩转二叉树 (25 分)

给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。所谓镜面反转,是指将所有非叶结点的左右孩子对换。这里假设键值都是互不相等的正整数。

输入格式:

输入第一行给出一个正整数N(≤30),是二叉树中结点的个数。第二行给出其中序遍历序列。第三行给出其前序遍历序列。数字间以空格分隔。

输出格式:

在一行中输出该树反转后的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。

输入样例:

7
1 2 3 4 5 6 7
4 1 3 2 6 5 7

输出样例:

4 6 1 7 5 3 2

我是在bfs里边实现了一下翻转,有点麻烦,能A就行。

#include 
#include 
#include 
#include 
#include 
#define debug(x) cout<<#x<<":"<<x<<endl
using namespace std;

const int N = 50;

int n,pre[N],in[N];
unordered_map<int,int>h,lson,rson;
int root;
int dfs(int inl,int inr,int pl,int pr)
{
    int root = pre[pl];
    int idx = h[root];
    if(idx>inl)
        lson[root]=dfs(inl,idx-1,pl+1,pl+idx-inl);
    if(idx<inr)
        rson[root]=dfs(idx+1,inr,pl+idx-inl+1,pr);
    return root;
}

void bfs(int S){
    queue<int>que;
    que.push(S);
    bool flag=1;
    while(que.size()){
        auto t = que.front();
        que.pop();  
        if(flag){
            flag=0;
            cout<<t;
        }
        else{
            cout<<" "<<t;
        }
        //输出的时候反转一下,感觉实现的有点麻烦
        if(lson.count(t)&&rson[t]){
            que.push(rson[t]);
        }
        else if(rson.count(t)&&rson[t]){
            que.push(rson[t]);
        }
        
        if(rson.count(t)&&lson[t]){
            que.push(lson[t]);
        }
        else if(lson.count(t)&&lson[t]){
            que.push(lson[t]);
        }
    }
}

int main(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>in[i],h[in[i]]=i;
    for(int i=1;i<=n;i++)cin>>pre[i];
    root = dfs(1,n,1,n);
    bfs(root);
    
}

笛卡尔树


学到了另一种要求输出层序遍历的方法,在dfs中加入深度,因为dfs按照根左右的顺序,顺便就把层序遍历序列记录下来了。

int n;
int a[35];

int get(int l,int r){ // 求[l,r]最小元素下标
	int res = l;
	for(int i=l;i<=r;i++){
		if(a[i] < a[res]){
			res = i;
		}
	}
	return res;
}

vector<int>ans[40];
void dfs(int l,int r,int u){
	if(u>=n || l>r)return ;
    int pos = get(l,r);// 
    int tar = a[pos];
    ans[u].pb(tar);
    dfs(l,pos-1,u+1);
    dfs(pos+1,r,u+1);
}

void solve(){
    cin>>n;
    fo(i,1,n){
        cin>>a[i];
    }
    dfs(1,n,1);
    for(int i=1;ans[i].size();i++){
    	for(auto c:ans[i]){
    		cout<<c<<" ";
    	}
    }
    

}

int main(){
    solve();
    return 0;
}   

你可能感兴趣的:(PAT天梯,深度优先,算法)