关于二叉树

果然每次写博客都是因为发现自己太菜

不过二叉树确实很难写到

最近写天梯赛发现自己不会 就学一下吧

二叉树

给出前序中序 求二叉树

分治 + 递归

建立前序和中序对于同一节点的对应关系

递归中序遍历

先找父节点(通过与前序的对应关系)

然后递归左子树和右子树

1
2
3
4
5
if(l>r) return null;
//找父节点位置
int x/*中序 中父节点位置*/ =mp[pre[fa]];
pa->left=so(fa+1/*前序中左子树的父节点位置*/,l,x-1/*中序中左子树区间*/);
pa->r=so(fa+x-l+1/*x-l 为利用中序求出的左子树长度,前序中父节点加上左子树长度即为右子树父节点位置*/,x+1,r/*中序中右子树区间*/);

题目链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include<bits/stdc++.h>
using namespace std;
int pre[35] = { 0 }, ino[35] = { 0 };
struct no {
int num, ls, rs;
};
no cnm[35];
map<int, int> mp;
int n;
vector<int> ans;
int cnt = 0;
int so(int p,int fa, int l, int r)
{
if (l > r) return -1;
int x = mp[pre[fa]];
cnm[p].num = pre[fa];
cnt++;
cnm[p].ls=so(cnt,fa + 1, l, x - 1);
cnt++;
cnm[p].rs=so(cnt,fa + x - l + 1, x + 1, r);
return p;

}
void sh()
{
queue<int> q;
q.push(0);
while (!q.empty())
{
int fa = q.front(); q.pop();
if (cnm[fa].rs != -1) q.push(cnm[fa].rs);
if (cnm[fa].ls != -1) q.push(cnm[fa].ls);
ans.push_back(cnm[fa].num);
}
for (int i = 0; i < n; i++)
{
if (i) cout << " ";
cout << ans[i];
}
return;
}
signed main()
{
cin >> n;
for (int i = 0; i < n; i++) cin >> ino[i], mp[ino[i]] = i;
for (int i = 0; i < n; i++) cin >> pre[i];
so(cnt,0, 0, n - 1);
sh();
return 0;
}

给后序和中序 求二叉树

跟上一种情况一样

题目链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include<bits/stdc++.h>
using namespace std;
int pos[35] = { 0 }, ino[35] = { 0 };
map<int, int> mp;
struct no {
int num, ls, rs;
};
no cnm[100];
vector<int> ans;
int n, cnt = 0;
int so(int p, int fa, int l, int r)
{
if (l > r) return -1;
int x = mp[pos[fa]];
cnm[p].num = pos[fa];
cnt++;
cnm[p].ls = so(cnt, fa+x-r-1, l, x - 1);
cnt++;
cnm[p].rs = so(cnt, fa - 1, x + 1, r);
return p;
}
void sh()
{
queue<int> q;
q.push(0);
while (!q.empty())
{
int fa = q.front(); q.pop();
if (cnm[fa].ls != -1) q.push(cnm[fa].ls);
if (cnm[fa].rs != -1) q.push(cnm[fa].rs);
ans.push_back(cnm[fa].num);
}
for (int i = 0; i < n; i++)
{
if (i) cout << " ";
cout << ans[i];
}
}
signed main()
{
cin >> n;
for (int i = 0; i < n; i++) cin >> pos[i];
for (int i = 0; i < n; i++) cin >> ino[i], mp[ino[i]] = i;


so(0, n-1, 0, n - 1);
sh();
return 0;
}

给完全二叉树的后序遍历 求二叉树

这个直接推比较麻烦,由于是完全二叉树,可以直接遍历

相当于先跑出节点个数为n的完全二叉树的后序遍历,然后把对应的数字填进去

题目链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<bits/stdc++.h>
using namespace std;
int a[35];
int n;
void dfs(int x)
{
if(x>=n) return ;
dfs(x*2+1);
dfs(x*2+2);
cin>>a[x];
}
signed main()
{
cin>>n;
dfs(0);
for(int i=0;i<n;i++)
{
if(i) cout<<" ";
cout<<a[i];
}
return 0;
}

二叉搜索树

一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点,

  • 其左子树中所有结点的键值小于该结点的键值;
  • 其右子树中所有结点的键值大于等于该结点的键值;
  • 其左右子树都是二叉搜索树

给出前序遍历问是否为二叉搜索树并还原

题目链接

先不考虑镜像情况

根据二叉树的前序遍历和二叉搜索树的性质 有

前序遍历

所以左子树区间为 ( fa + 1 , x ) x 为从右向左数第一个小于fa值的数的下标

右子树区间为 ( y , r ) y为从左向右数第一个大于等于fa值的数的下标

并且x == r || y == fa+1 || x == y-1 //无右子树 无左子树 有左右子树

然后递归求解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include<bits/stdc++.h>
using namespace std;
int a[1005];
int n;
vector<int> ans;

bool s1(int l,int r)
{
if(l>r) return 1;
int fa=l;
l++;
int x=r,y=l;
while(x>=l&&a[x]>=a[fa]) x--;
while(y<=r&&a[y]<a[fa]) y++;
//因为是后序遍历,需要先push_back子节点,所以定义变量最后return
bool f=1,ff=1;
if(x>r||y<l) f=s1(fa+1,r);

else
{
if(y!=x+1) return 0;
f=s1(fa+1,x);
ff=s1(y,r);
}

ans.push_back(a[fa]);
return f&&ff;
}
bool s2(int l,int r)
{
if(l>r) return 1;
int fa=l;
l++;
int x=r,y=l;
while(x>=l&&a[x]<a[fa]) x--;
while(y<=r&&a[y]>=a[fa]) y++;

bool f=1,ff=1;
if(x<l||y>r) f=s2(fa+1,r);

else
{
if(y!=x+1) return 0;
f=s2(fa+1,x);
ff=s2(y,r);
}

ans.push_back(a[fa]);
return f&&ff;
}
void sh()
{
cout<<"YES\n";
for(int i=0;i<n;i++)
{
if(i) cout<<" ";
cout<<ans[i];
}
return;
}
signed main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];

if(s1(0,n-1)) {sh();return 0;}
ans.clear();
if(s2(0,n-1)) {sh();return 0;}
cout<<"NO\n";
return 0;
}

给一个序列插入二叉搜索树求树的结构

就直接模拟完事

题目链接1

如何判断一棵树是不是完全二叉树

对于层序遍历结果而言,找到第一个不饱和节点后,后续的节点全没儿子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include<bits/stdc++.h>
using namespace std;
struct no{
int num,ls,rs;
};
no cnm[25];
void so(int fa,int i)
{
if(cnm[fa].num<cnm[i].num)
{
if(cnm[fa].ls==-1) {cnm[fa].ls=i;return ;}
else so(cnm[fa].ls,i);
}
else
{
if(cnm[fa].rs==-1){cnm[fa].rs=i;return ;}
else so(cnm[fa].rs,i);
}
return;
}
bool sh()
{
vector<int> ans;
queue<int> q;
q.push(0);
while(!q.empty())
{
int fa=q.front();q.pop();
if(cnm[fa].ls!=-1) q.push(cnm[fa].ls);

if(cnm[fa].rs!=-1) q.push(cnm[fa].rs);
ans.push_back(fa);
}
int l=ans.size();
for(int i=0;i<l;i++)
{
if(i) cout<<" ";
cout<<cnm[ans[i]].num;
}


int i = 0;
for (; i < l; i++)
{
if (cnm[ans[i]].ls != -1 && cnm[ans[i]].rs != -1);
else break;
}
//注意这里要判断是否存在无左儿子但有右儿子的情况
if(cnm[ans[i]].ls==-1&&cnm[ans[i]].rs != -1) return 0;
i++;
for (; i < l; i++)
if (cnm[ans[i]].ls != -1 || cnm[ans[i]].rs != -1) return 0;
return 1;
}
signed main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>cnm[i].num,cnm[i].ls=cnm[i].rs=-1;
for(int i=1;i<n;i++)
so(0,i);

if(sh())
cout<<"\nYES";
else cout<<"\nNO";
return 0;
}

题目链接2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include<bits/stdc++.h>
using namespace std;
struct no {
int num, ls, rs, c, fa;
};
map<int, int> mp;
no cnm[105];
void so(int fa, int i)
{
if (cnm[fa].num > cnm[i].num)
{
if (cnm[fa].ls == -1) { cnm[fa].ls = i; cnm[i].fa = fa; cnm[i].c = cnm[fa].c + 1; return; }
else so(cnm[fa].ls, i);
}
else
{
if (cnm[fa].rs == -1) { cnm[fa].rs = i; cnm[i].fa = fa; cnm[i].c = cnm[fa].c + 1; return; }
else so(cnm[fa].rs, i);
}
return;
}
string s;
int to_num(int& i)
{
int l = s.size();
int x = 0;
int ff = 1;
for (; i < l; i++) if (s[i] <= '9' && s[i] >= '0' || s[i] == '-') break;
if (s[i] == '-') ff = -1, i++;
while (i < l && s[i] <= '9' && s[i] >= '0')
{
x = x * 10 + s[i] - '0';
i++;
}
return x * ff;
}
signed main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> cnm[i].num, mp[cnm[i].num] = i, cnm[i].ls = cnm[i].rs = -1;
cnm[1].fa = 0, cnm[1].c = 1;
for (int i = 2; i <= n; i++)
so(1, i);

/*for(int i=1;i<=n;i++)
cout<<cnm[i].num<<" "<<cnm[i].ls<<" "<<cnm[i].rs<<" "<<cnm[i].fa<<" "<<cnm[i].c<<"\n";*/
int m;
cin >> m;
getchar();

while (m--)
{
int a, b, i = 0;
getline(cin, s);
if (s.find("root") != string::npos)
{
a = to_num(i);
if (mp[a] == 1) cout << "Yes\n";
else cout << "No\n";
}
else
{
a = to_num(i);
b = to_num(i);
if (!mp[a] || !mp[b]) { cout << "No\n"; continue; }
if (s.find("siblings") != string::npos)
{
if (mp[a] != mp[b] && cnm[mp[a]].fa == cnm[mp[b]].fa)cout << "Yes\n";
else cout << "No\n";
}
else if (s.find("parent") != string::npos)
{
if (cnm[mp[b]].fa == mp[a])cout << "Yes\n";
else cout << "No\n";
}
else if (s.find("left") != string::npos)
{
if (cnm[mp[b]].ls == mp[a]) cout << "Yes\n";
else cout << "No\n";
}
else if (s.find("right") != string::npos)
{
if (cnm[mp[b]].rs == mp[a]) cout << "Yes\n";
else cout << "No\n";
}
else if (s.find("level") != string::npos)
{
if (cnm[mp[a]].c == cnm[mp[b]].c)cout << "Yes\n";
else cout << "No\n";
}
}


}
return 0;
}