数据结构——二叉树

非递归二叉树的前序遍历 计蒜客 - 141

非递归二叉树的中序遍历 计蒜客 - 144

非递归二叉树的后序遍历 计蒜客 - 140

问题描述

给定一个层数小于等于 10 的二叉树,输出对其遍历的节点名序列。

输入包括一行,为由空格分隔开的各节点,按照二叉树的分层遍历顺序给出,
每个节点形式如 X(Y,num),X 表示该节点,Y 表示父节点,num 为 0,1,2 中的一个,
0 表示根节点,1 表示为父节点的左子节点,2 表示为父节点的右子节点。

【这里我将题目合在一起了】
输出为三行,为前序遍历,中序遍历,后序遍历的结果。

输入样例:

A(0,0) B(A,1) C(A,2) D(B,1) E(B,2) F(C,1) G(D,1) H(D,2)

输出样例:

A B D G H E C F
G D H B E A F C
G H D E B F C A
  • 参考程序
#include<iostream>
#include<cstdio>
using namespace std;
const int N=(1<<10);
char a[100], tree[N];
int n=0, m=0;
void preorder(char root){
    if(root=='\0') return;
    if(tree[0]==root) printf("%c", root);
    else printf(" %c", root);
    preorder(tree[2*root]);
    preorder(tree[2*root+1]);
}
void inorder(char root){
    if(root=='\0') return;
    inorder(tree[2*root]);
    if(m==n-1) {
        printf("%c", root); m++;
    } else {
        printf("%c ", root); m++;
    }
    inorder(tree[2*root+1]);
}
void postorder(char root){
    if(root=='\0') return;
    postorder(tree[2*root]);
    postorder(tree[2*root+1]);
    if(tree[0]==root) printf("%c", root);
    else printf("%c ", root);
}
int main(){
//    freopen("data.in", "r", stdin);
    while(~scanf("%s", a)){
        char x,y,num; sscanf(a, "%c(%c,%c)", &x,&y,&num);
        if(num=='0'){
            tree[0] = x;
        }else if(num=='1'){
            tree[2*y] = x;// y是单个字符,在内存中以ASCII码的形式进行存储
        }else if(num=='2'){
            tree[2*y+1] = x;
        }
    }
    preorder(tree[0]); printf("\n");
    inorder(tree[0]);  printf("\n");
    postorder(tree[0]);printf("\n");
    return 0;
}

二叉树中序遍历 51Nod - 2064

问题描述

输入一个整数n(n <= 100000),表示二叉树中节点个数,编号为1~n。
约定1号节点为二叉树的根节点。
然后输入n行,每行包括两个整数,第i行表示编号为i的节点的左子节点和右子节点的编号。
如果某个节点没有左子节点,那么对应输行的第一个整数为0;
如果某个节点没有右子节点,那么对应行的第二个整数为0。
中序遍历输出此二叉树每个节点的编号,每行输出一个编号。

输入格式:第一行,一个整数n,接下来n行,每行有两个整数。
输出格式:输出n行,每行一个整数,表示节点编号。

输入样例

5
2 5
3 4
0 0
0 0
0 0

输出样例

3
2
4
1
5
  • 参考程序
#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e5+0;
struct T{
    int l,r;
}tree[N];
void inorder(int m){
    if(m==0) return;
    inorder(tree[m].l);
    printf("%d\n", m);
    inorder(tree[m].r);
}
int main(){
    int n; scanf("%d", &n);
    for(int i=1; i<=n; i++) {
        scanf("%d%d", &tree[i].l, &tree[i].r);
    }
    inorder(1);    
    return 0;
}
  • 下列程序wa了,待调试
#include<iostream>
#include<cstdio>
using namespace std;
const int N=2e5+10;
int tree[N];
void inorder(int i){
    if(tree[i]==0) return;
    inorder(2*i);
    printf("%d\n", tree[i]);
    inorder(2*i+1);
}
int main(){
    int n; scanf("%d", &n);
    tree[1] = 1;
    for(int i=1; i<=n; i++){
        int lch, rch; scanf("%d%d", &lch,&rch);
        tree[2*i] = lch;
        tree[2*i+1] = rch;
    }
    inorder(1);
    return 0;
}

求先序排列 洛谷 - P1030

问题描述

给出一棵二叉树的中序与后序排列,求出它的先序排列。
(约定树结点用不同的大写字母表示,长度 ≤8)。

输入格式:2行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。
输出格式:1行,表示一棵二叉树的先序。

输入样例

BADC
BDCA

输出样例

ABCD
  • 参考程序
#include<iostream>
#include<cstdio>
using namespace std;
struct Node{
    char data;
    struct Node *lch,*rch;
};
string in,post;
Node* build(int pl,int pr,int il,int ir){
    if(pl>pr) return NULL;
    Node* root = new Node;
    root->data = post[pr];
    int k=il;
    while(post[pr]!=in[k] && k<=ir) k++;
    root->lch = build(pl,pl+k-il-1,il,k-1);
    root->rch = build(pl+k-il,pr-1,k+1,ir);
    return root;
}
void preorder(Node* root){
    if(root==NULL) return;
    cout<<root->data;
    preorder(root->lch);
    preorder(root->rch);
}
int main(){
    cin>>in>>post; 
    Node* root=build(0, post.size()-1, 0, in.size()-1); 
    preorder(root);
    return 0;
}
  • 参考程序
#include<iostream>
#include<cstdio>
using namespace std;
void dfs(string bs, string cs){
    if(bs.length()==0) return;
    char root = cs[cs.length()-1];
    int id = bs.find(root);
/*  bs: B A DC
    cs: BDC A
    str.stustr(i,j);   截取 str从下标 i开始,j个数据
    str.substr(i);     截取 str从下标 i开始,到最后
    str.find((char)c); 在str中查找第一个字符c的位置,返回下标,如果没有返回string::npos */
    cout<<root;
    dfs(bs.substr(0,id), cs.substr(0,id));//左
    dfs(bs.substr(id+1), cs.substr(id, bs.length()-id-1));//右
}
int main(){
    string as,bs,cs;
    cin>>bs>>cs;
    dfs(bs, cs);
    return 0; 
} 

新二叉树 洛谷 - P1305

问题描述
输入一串二叉树,输出其前序遍历。

输入格式
第一行为二叉树的节点数 n(1≤n≤26)。
后面 n 行,每一个字母为节点,后两个字母分别为其左右儿子。
空节点用 * 表示

输出格式:二叉树的前序遍历。

输入样例:

6
abc
bdi
cj*
d**
i**
j**

输出样例:

abdicj
  • 参考程序
#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e5+10;
char tree[N][2], root; 
void dfs(char x){
    if(root=='*') return;
    cout<<x;
    dfs(tree[x][0]);
    dfs(tree[x][1]);
}
int main(){
    int n; cin>>n;
    for(int i=1; i<=n; i++){
        char a,b,c; cin>>a>>b>>c;
        if(i==1)  root=a;//确定根节点 
        tree[a][0]=b, tree[a][1]=c;
    }
    dfs(root);
    return 0;
}

遍历问题 洛谷 - P1229

问题描述

我们都很熟悉二叉树的前序、中序、后序遍历,在数据结构中常提出这样的问题:
已知一棵二叉树的前序和中序遍历,求它的后序遍历,
相应的,已知一棵二叉树的后序遍历和中序遍历序列你也能求出它的前序遍历。
然而给定一棵二叉树的前序和后序遍历,你却不能确定其中序遍历序列,

输入格式:
输A数据共两行,第一行表示该二叉树的前序遍历结果s1,
第二行表示该二叉树的后序遍历结果s2。

输出格式:输出可能的中序遍历序列的总数,结果不超过长整型数。
输入样例:

abc
cba

输出样例:4

  • 分析

确定中序遍历的数量在于含有一个孩子结点的结点个数 N,而可能是左右孩子,所以最后结果为 2^N。

  • 参考程序
#include<bits/stdc++.h>
using namespace std;
int main(){
    string as,cs; cin>>as>>cs;
    int ans=0;
    for(int i=0; i<as.length(); i++){
        for(int j=1; j<cs.length(); j++){
            // ab - ba 
            if(as[i]==cs[j]&&as[i+1]==cs[j-1]) ans++;
        }
    }
    cout<<(1<<ans);
    return 0;
}
 
  • 思考改进

对于已知先后遍历结果,可以求出中序遍历的方案数,输出任意一种中序遍历结果。


美国血统 American Heritage 洛谷 - P1827

问题描述

农夫约翰非常认真地对待他的奶牛们的血统。
然而他不是一个真正优秀的记帐员。
他把他的奶牛们的家谱作成二叉树,并且把二叉树以更线性的“树的中序遍历”
和“树的前序遍历”的符号加以记录而不是用图形的方法。

你的任务是在被给予奶牛家谱的“树中序遍历”和“树前序遍历”的符号后,
创建奶牛家谱的“树的 后序遍历”的符号。
每一头奶牛的姓名被译为一个唯一的字母。
(你可能已经知道你可以在知道树的两 种遍历以后可以经常地重建这棵树。)
显然,这里的树不会有多于 26 个的顶点。
这是在样例输入和 样例输出中的树的图形表达方式:

    C
      / \ 
    /   \
  B    G
  / \  /
  A   D  H
    / \
   E  F

输入格式
第一行: 树的中序遍历
第二行: 同样的树的前序遍历

输出格式:单独的一行表示该树的后序遍历。

输入样例

ABEDFCHG
CBADEFGH 

输出样例

AEFDBHGC
  • 参考程序
#include<bits/stdc++.h>
using namespace std;
string as, bs, cs;
void dfs(string bs, string as){
    if(bs.length()==0) return;
    char root = as[0];
    int id = bs.find(root);
/*  bs: ABEDF C HG
    as: C BADEFGH   -> dfs("ABEDF", "BADEF"); 
                       dfs("HG", "GH");  
    str.stustr(i,j);  截取 str从下标 i开始,j个数据
    str.substr(i);    截取 str从下标 i开始,到最后 
    str.find((char)c); 在str中查找第一个字符c的位置,返回下标,如果没有返回string::npos */
    dfs(bs.substr(0,id), as.substr(1,id));//左 
    dfs(bs.substr(id+1), as.substr(id+1));//右 
    cout<<root;
}
int main(){
    cin>>bs>>as;
    dfs(bs, as);
    return 0; 
} 

淘汰赛 洛谷 - P4715

问题描述

有 2^n (n≤7) 个国家参加世界杯决赛圈且进入淘汰赛环节。
已经知道各个国家的能力值,且都不相等。
能力值高的国家和能力值低的国家踢比赛时高者获胜。
1 号国家和 2 号国家踢一场比赛,胜者晋级。
3 号国家和 4 号国家也踢一场,胜者晋级…
晋级后的国家用相同的方法继续完成赛程,直到决出冠军。
给出各个国家的能力值,请问亚军是哪个国家?

输入格式
第一行一个整数 n,表示一共 2^n个国家参赛。
第二行 2^n 个整数,第 i 个整数表示编号为 i 的国家的能力值(1≤i≤2^n)。
数据保证不存在平局。

输出格式:仅一个整数,表示亚军国家的编号。

输入样例:

3
4 2 3 1 10 5 9 7

输出样例:1

  • 参考程序
#include<iostream>
#include<cmath> 
#include<queue>
#include<map>
using namespace std;
int main() {
	int n; cin>>n;
	n = pow(2, n);// n=1<<n;
	queue<pair<int, int> > que; // pair就是一个结构体 
	for(int i=1; i<=n; i++) {
		int a; cin>>a;
		que.push(make_pair(i, a));	
	}
	while( que.size() > 2 ){
		pair<int, int> x,y; 
		x = que.front();
		que.pop();
		y = que.front();
		que.pop();
		if(x.second > y.second){
			que.push(x);
		}else{
			que.push(y);
		}
	}
	
	pair<int, int> x,y; 
	x = que.front(); que.pop();
	y = que.front(); que.pop();
	if(x.second > y.second) cout<<y.first;
	else cout<<x.first;
	return 0;
}

二叉树深度 洛谷 - P4913

问题描述

给出每个节点的两个儿子节点,建立一棵二叉树(根节点为 1),
如果是叶子节点,则输入 0。
建好树后希望知道这棵二叉树的深度。
二叉树的深度是指从根节点到叶子结点时,最多经过了几层。

最多有 10^6 个结点。

输入格式
第一行一个整数 n,表示节点数。
之后 n 行,第 i 行两个整数 l、r,分别表示节点 i 的左右子节点。
若 l=0 则表示无左子节点,r=0 同理。

输出格式:一个整数,表示最大节点深度。

输入样例:

7
2 7
3 6
4 5
0 0
0 0
0 0
0 0

输出样例:4

  • 参考程序
#include<bits/stdc++.h>
using namespace std;
const int N=1e7;
struct T{
    int l,r;
}node[N]; 
int ans=1, depth[N];
void dfs(int m){//遍历当前节点元素 m 
    if(m==0) return;//叶子节点
    depth[node[m].l] = depth[m]+1;
    dfs(node[m].l);//遍历左节点 
    
    depth[node[m].r] = depth[m]+1;
    dfs(node[m].r);//遍历右节点 
}
int main() {
    ios::sync_with_stdio(false);//关闭流同步,加速 
    int n; cin>>n;
    for(int i=1; i<=n; i++){
        cin>>node[i].l>>node[i].r;
    }
    depth[1]=1;
    dfs(1);
    for(int i=1; i<=n; i++){
        if(ans<depth[i]) ans=depth[i];
    }
    cout<<ans;
	return 0;
}

二叉树结点公共祖先 CSG - 1012

问题描述

一个顺序存储的完全二叉树:

            1
          /   \
        2       3
      /   \    / \
    4      5  6   7
    ...

任意给定两结点的编号,求两结点最近的公共祖先。

输入格式
每组数据一行,为空格隔开的两个数i和j,皆为32位有符号正整数

输出格式
每组数据对应一行,为编号为i和j的结点的最近公共祖先的编号

输入样例:

4 5
4 7

输出样例:

2
1
  • 参考程序
#include<iostream>
#include<cstdio>
using namespace std;
const int N=2e5+10;
int tree[N], depth[N];
void dep(int u){
    if(2*u>N) return;
    depth[2*u] = depth[2*u+1] = depth[u]+1;
    dep(2*u);
    dep(2*u+1);
}
int LCA(int u,int v){
    if(u==v) return u;
    if(depth[u]== depth[v]) return LCA(u/2, v/2);
    if(depth[u] < depth[v]) return LCA(u, v/2);
    if(depth[u] > depth[v]) return LCA(u/2, v);
}
int main(){
//    freopen("data.in", "r", stdin);
    dep(1);
    int u,v;
    while(cin>>u>>v){
        cout<<LCA(u,v)<<endl;
    }
    return 0;
}

二叉树 OpenJ_Bailian - 2756

问题描述

            1
          /   \
        2       3
      /   \    / \
    4      5  6   7
    ...

如上图所示,由正整数1, 2, 3, …组成了一棵无限大的二叉树。

从某一个结点到根结点(编号是1的结点)都有一条唯一的路径,
比如从10到根结点的路径是(10, 5, 2, 1),从4到根结点的路径是(4, 2, 1),从根结点1到根结点的路径上只包含一个结点1,因此路径就是(1)。

对于两个结点x和y,假设他们到根结点的路径分别是(x1, x2, … ,1)和(y1, y2, … ,1)(这里显然有x = x1,y = y1),那么必然存在两个正整数i和j,使得从xi 和 yj开始,有xi = yj , xi + 1 = yj + 1, xi + 2 = yj + 2,… 现在的问题就是,给定x和y,要求xi(也就是yj)。

输入格式
输入只有一行,包括两个正整数x和y,这两个正整数都不大于1000。

输出格式
输出只有一个正整数xi。

输入样例:

10 4

输出样例:

2
  • 参考程序
  • 啊啊啊!
    突然发现这个题不就是求最近公共祖先嘛,和上个题目是一样的,程序都是一模一样!

二叉树 OpenJ_Bailian - 2788

问题描述

            1
          /   \
        2       3
      /   \    / \
    4      5  6   7
    ...

如上图所示,由正整数1,2,3……组成了一颗二叉树。
我们已知这个二叉树的最后一个结点是n。
现在的问题是,结点m所在的子树中一共包括多少个结点。

比如,n = 12,m = 3
那么上图中的结点13,14,15以及后面的结点都是不存在的,
结点m所在子树中包括的结点有3,6,7,12,
因此结点m的所在子树中共有4个结点。

输入格式
输入数据包括多行,每行给出一组测试数据,包括两个整数m,n (1 <= m <= n <= 1000000000)。
最后一组测试数据中包括两个0,表示输入的结束,这组数据不用处理。

输出格式
对于每一组测试数据,输出一行,该行包含一个整数,给出结点m所在子树中包括的结点的数目。

输入样例:

3 12
0 0

输出样例:

4
  • 参考程序
  • 递归超时,转递推发现空间爆炸。
  • 正解:根据左孩子,右孩子的最深层的位置推导。
  • 直觉有一个公式,但是没推导出来
  • 依据:第 k k k 层的满二叉树结点数 为 2 k − 1 2^{k-1} 2k1,一棵满二叉树的结点数为 2 k − 1 2^k-1 2k1
#include<iostream>
#include<cmath>
#include<cstdio>
using namespace std;
const int N=1e6+10;
int count(int u,int v){ // TLE
    if(u>v) return 0;
    return count(2*u, v)+count(2*u+1, v)+1;
}
int count2(int u,int v){
    int lch=u, rch=u, cnt=0;
    int k=1; // 以 u为根节点的树的层数 
    while(rch <= v){
        cnt += pow(2, k-1);
        lch = 2*lch;
        rch = 2*rch+1;
        k++;
    }
    if(lch > v) return cnt;
    else return cnt+v-lch+1;//lch被减,所以+1 
} 
int main(){
    freopen("data.in", "r", stdin);
    int u,v;
    while(cin>>u>>v){
        if(u==0&&v==0) break;
        cout<<count2(u,v)<<endl;
    }
    return 0;
}

下午茶时间 计蒜客 - T1726

问题描述

N(1≤N≤1000) 头奶牛,编号为 1…N,在参加一个喝茶时间活动。

在喝茶时间活动开始之前,已经有 M(1≤M≤2,000) 对奶牛彼此认识(是朋友)。
第 i 对彼此认识的奶牛通过两个不相同的整数 Ai 和 Bi,给定(1 ≤Ai ≤N; 1 ≤Bi ≤N)。

输入数据保证一对奶牛不会出现多次。
在喝茶时间活动中,如果奶牛 i 和奶牛 j 有一个相同的朋友奶牛 k,
那么他们会在某次的喝茶活动中去认识对方(成为朋友),从而扩大他们的社交圈。
请判断,在喝茶活动举办很久以后(直到没有新的奶牛彼此认识),
Q(1≤Q≤100) 对奶牛是否已经彼此认识。

询问 j 包含一对不同的奶牛编号 Xj 和 Yj (1≤Xj ≤N;1≤Yj ≤N)。

例如,假设共有 1…5 头奶牛,我们知道 2 号认识 5 号,2 号认识 3 号,而且 4 号认识 5号。

输入样例

5 3 3 
2 5 
2 3 
4 5 
2 3 
3 5 
1 5 

输出样例

Y 
Y 
N 
  • 参考程序
#include<iostream>
#include<cstdio>
const int N=1e5+10;
int f[N], n,m,x,y,q;
int find(int x){
    return x==f[x] ? x : (f[x]=find(f[x]));
}
void merge(int x,int y){
    f[find(x)] = f[find(y)];
}
int main(){
    scanf("%d%d%d", &n, &m, &q);
    for(int i=1; i<=n; i++) f[i]=i;
    for(int i=1; i<=m; i++){
        scanf("%d%d", &x, &y); merge(x,y);
    }
    for(int i=1; i<=q; i++){
        scanf("%d%d", &x, &y);
        printf("%s\n", find(x)==find(y) ? "Y":"N");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值