二叉树
树叶子节点PAT1004
#include <iostream>
#include <cstring>
#include<algorithm>
using namespace std;
const int N = 110;
int n,m;
//用数组模拟链表模拟树
int e[N],ne[N],head[N],idx;
int max_height;
int every_height_leaf[N];
void add(int a,int b)
{
e[idx] = b;
ne[idx] = head[a];
head[a] = idx;
idx++;
}
void dfs(int node,int depth)
{
max_height = max(max_height,depth);
if(head[node]==-1){
every_height_leaf[depth]++;
return;
}
for(int i=head[node];i!=-1;i=ne[i])
{
int now_node = e[i];
dfs(now_node,depth+1);
}
}
int main()
{
cin >> n >> m;
memset(head,-1,sizeof head);
while(m--)
{
int id,k;
cin >> id >> k;
for(int i=0;i<k;i++){
int num;
cin >> num;
add(id,num);
}
}
dfs(1,0);
for(int i=0;i<=max_height;i++) cout << every_height_leaf[i]<<" ";
return 0;
}
树的遍历PAT1020
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<unordered_map>
using namespace std;
const int N = 40;
int n;
int inorder[N],postorder[N]; //记录中序和后续的顺序
unordered_map<int,int> l,r,pos; //pos记录在中序遍历中i节点的下标
int build(int in_l,int in_r,int post_l,int post_r)
{
int root = postorder[post_r];
int k = pos[root];//在中序遍历中根节点的位置
if(k>in_l) l[root] = build(in_l,k-1,post_l,post_l+(k-1-in_l));
if(k<in_r) r[root] = build(k+1,in_r,post_l+(k-1-in_l)+1,post_r-1);
return root;
}
int main()
{
cin >> n;
for(int i=0;i<n;i++) cin >> postorder[i];
for(int i=0;i<n;i++){
cin >> inorder[i];
pos[inorder[i]] = i;
}
int the_root = build(0,n-1,0,n-1);
queue<int> q;
q.push(the_root);
int head = q.front();
q.pop();
if(l[head]) q.push(l[head]);
if(r[head]) q.push(r[head]);
cout << head;
while(!q.empty())
{
int now_node = q.front();
q.pop();
if(l[now_node]) q.push(l[now_node]);
if(r[now_node]) q.push(r[now_node]);
cout <<" " <<now_node;
}
return 0;
}
c++中的set就是由二叉搜索树实现的
PAT1024
这个题给了一个序列,这个序列可能是一个BST的前序序列或者是其镜像的前序序列
由于一个BST的中序序列一定是有序的,所以我们思想就是,假定给的序列是正确的前序序列,看看能不能构造出一个树
如果是正确的,那么一定可以构造出一个树,反之,则不可以
而对于BST的镜像,如果给的序列是BST镜像的前序遍历,那么其中序遍历就是从大到小的一个排列了
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n;
int preorder[N],inorder[N],postorder[N];
int cnt;
//镜像之前中序遍历是从小到大的顺讯,镜像之后的中序遍历是一个从大到小的顺序
//对于这个BST左子树的值是严格小于根节点的
//所以比如 1 2 3 4 5 5 6 7 8这个中序遍历,有两个5,那就只能认为第一个5是根节点了
//如果第二个5作为根节点,那么左子树就不严格小于根节点了
//反之对于镜像的8 7 6 5 5 4 3 2 1 我们找的是最后一个5
bool build(int il,int ir,int prel,int prer,int type)//type表示输入的是镜像还是正常
{
//如果当前区间没有元素了,那么就可以重建
if(il>ir) return true;
//看看根节点能在中序序列中找到吗
//因为如果是一个树的正确前序序列,那么在中序中一定能找到
int root = preorder[prel];
int k; //k是根节点在中序中的位置
if(type==0)
{
for(k = il;k<=ir;k++)
{
if(inorder[k]==root) break;
}
if(k>ir) return false;
}
else{
for(k=ir;k>=il;k--)
{
if(inorder[k]==root) break;
}
if(k<il) return false;
}
//找到根节点后,看看左右子树能不能构建成功
bool res = true;
if(!build(il,k-1,prel+1,prel+1+k-1-il,type)) res = false; //先建立左子树
if(!build(k+1,ir,prel+1+k-1-il+1,prer,type)) res = false; //再建立右子树
postorder[cnt] = root; //这条语句是慢执行于44,45行这两条语句的,所以是先给postorder数组找到左右子树再赋值根节点
cnt++;
return res;
}
int main()
{
cin >> n;
for(int i=0;i<n;i++)
{
cin >> preorder[i];
inorder[i] = preorder[i];
}
sort(inorder,inorder+n);
cnt = 0;
if(build(0,n-1,0,n-1,0))
{
puts("YES");
cout<<postorder[0];
for(int i=1;i<n;i++) cout<<" "<<postorder[i];
}
else{//如果没成功,那么我们要考虑是不是镜像的前序了
reverse(inorder,inorder+n);
cnt = 0;//这个cnt=0必须写,因为来到这个分支,之前已经执行过一个build了,cnt已经变了
if(build(0,n-1,0,n-1,1))
{
puts("YES");
cout<<postorder[0];
for(int i=1;i<n;i++) cout<<" "<<postorder[i];
}
else{
puts("NO");
}
}
return 0;
}
PAT1064
完全二叉树的好处就是他用一个一维数组就可以存下n个节点了,下标从1-n
i号节点的左儿子是2i,右儿子是2i+1
并且层序遍历就是这个数组从1-n的遍历
如果我们要构造一个完全二叉搜索树,那么
- 构造一个完全二叉树
- 这些节点有序序列就是二叉搜索树的中序遍历,那么我们把中序遍历的这个序列填进去就好了
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n;
int node[N]; //记录权重,之后经过排序是存储的中序序列
int bst[N]; //记录构造的BST下标从1开始
void dfs(int v,int& k)
{
if(2*v<=n) dfs(2*v,k); //找到左子树
bst[v] = node[k]; //给根节点赋值
k++;
if(2*v+1 <= n) dfs(2*v+1,k); //找到右子树
}
int main()
{
cin >> n;
for(int i=0;i<n;i++) cin >> node[i];
sort(node,node+n);
int k=0;
dfs(1,k);
for(int i=1;i<=n;i++) cout<<bst[i]<<" ";
return 0;
}
PAT1086
#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;
const int N = 40;
int n;
stack<int> st;
int type = -1; //type记录上一次操作是push还是pop
//0表示是pop,1表示是push,-1代表还没操作
//因为根据非递归写法,上次操作是push的话这次push的节点是上个节点的左儿子
//如果上次操作是pop的话,这次push的节点是上次节点的右儿子
int root; //记录根节点是哪个
int l[N],r[N]; //记录节点的左右儿子
int father; //记录当前操作的上一次操作的节点号
//后序遍历输出,左右根
//后序遍历最后一个节点一定是根节点,由于PAT不能有行末空格
//所以判断当前点是不是根节点来确定加不加空格
void dfs(int u,int root)
{
if(!u) return; //没有左右儿子值是0
dfs(l[u],root);
dfs(r[u],root);
cout << u;
if(u!=root) cout<<" ";
}
int main()
{
cin >> n;
for(int i=0;i<n*2;i++)
{
string op;
cin >> op;
if(op=="Push")
{
int num;
cin >> num;
if(type==-1)
{
root = num;
}
else if(type==0)//上次是pop
{
r[father] = num;
}
else if(type==1){
l[father] = num;
}
father = num;
type = 1;
st.push(num);
}
else if(op=="Pop")
{
type = 0;
father = st.top();
st.pop();
}
}
dfs(root,root);
return 0;
}
PAT1099
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 110;
int n;
int l[N],r[N];
int read[N],tree[N]; //read数组记录中序遍历结果,tree是我们最终构建的树
//进行中序遍历,k是当前要从read中填入的数的下标
void dfs(int u,int &k)
{
if(u == -1) return;
dfs(l[u],k);
tree[u] = read[k];
k++;
dfs(r[u],k);
}
queue<int> q;
void BFS(int u)
{
q.push(u);
while(!q.empty())
{
int node = q.front();
q.pop();
if(l[node]!=-1){
q.push(l[node]);
}
if(r[node]!=-1){
q.push(r[node]);
}
cout << tree[node] << " ";
}
}
int main()
{
memset(l,-1,sizeof l);
memset(r,-1,sizeof r);
cin >> n;
for(int i=0;i<n;i++) cin >> l[i] >> r[i];
for(int i=0;i<n;i++) cin >> read[i];
sort(read,read+n);
int k = 0; //这里k一定要赋值0,不要就写个int k; 否则会有段错误
int root = 0; //这里根节点是0号位置
dfs(root,k);
BFS(root);
return 0;
}
PAT1102
这里翻转的意思就是把树关于中轴线做个轴对称,那么翻转我们只要把左右儿子进行交换就好了
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 20;
int n;
int l[N],r[N];
bool have_father[N]; //由于题目没说哪个是根节点,所以需要求出父节点是哪个
void dfs_reverse(int u)
{
if(u==-1) return;
dfs_reverse(l[u]);
dfs_reverse(r[u]);
swap(l[u],r[u]);
}
queue<int> q;
void bfs(int u)
{
q.push(u);
while(!q.empty())
{
int node = q.front();
q.pop();
if(l[node]!=-1) q.push(l[node]);
if(r[node]!=-1) q.push(r[node]);
cout << node <<" ";
}
cout <<endl;
}
void dfs_inorder(int u)
{
if(u==-1) return;
dfs_inorder(l[u]);
cout << u <<" ";
dfs_inorder(r[u]);
}
int main()
{
memset(l,-1,sizeof l);
memset(r,-1,sizeof r);
cin >> n;
for(int i=0;i<n;i++)
{
char lop,rop;
cin >> lop >> rop;
if(lop != '-'){
l[i] = lop - '0';
have_father[l[i]] = true;
}
if(rop != '-'){
r[i] = rop - '0';
have_father[r[i]] = true;
}
}
int root = 0;
for(int i=0;i<n;i++){
if(have_father[i]){
root++;
}
else{
break;
}
}
//把二叉树翻转只要交换每个节点的左右子树就好了
dfs_reverse(root);
bfs(root);
dfs_inorder(root);
return 0;
}
完全二叉树PAT1110
对于一个完全二叉树,如果是完全二叉树的话,一定可以把这个数存到一个一维数组中的1~n号位置中
如果他不能够存到一维数组的1~n号位置中,那么他就不是完全二叉树
思路就是如果不是完全二叉树,那么会假象用空节点补全成完全二叉树,那么最后构造出的最后一个节点在
一维数组中的下标一定是大于n的
#include <iostream>
#include <cstring>
using namespace std;
const int N = 25;
int n;
int l[N],r[N];
bool have_father[N];
int maxk,lastid; //maxk表示假设是个完全二叉树,一维数组的最大下标号
//maxid表示最后一个节点的ID是啥
void dfs(int u,int k) //k表示u号节点在一维数组中的下标位置
{
if(u==-1) return;
if(k>maxk){
maxk = k;
lastid = u;
}
dfs(l[u],2*k);
dfs(r[u],2*k+1);
}
int main()
{
memset(l,-1,sizeof l);
memset(r,-1,sizeof r);
cin >> n;
for(int i=0;i<n;i++)
{
string a,b;
cin >> a >> b;
if(a!="-"){
l[i] = stoi(a);
have_father[l[i]] = true;
}
if(b!="-"){
r[i] = stoi(b);
have_father[r[i]] = true;
}
}
int root = 0;
while(have_father[root]) root++;
dfs(root,1);
if(maxk==n){
printf("YES %d",lastid);
}
else{
printf("NO %d",root);
}
return 0;
}
PAT1115
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1010;
int n;
int l[N],r[N],e[N],idx; //idx记录当前可用的节点编号
void insert(int& u,int v) //由于这里写了引用u所以,左右儿子的节点会自动更新成下标
{
if(u==0){
idx++;
u = idx;
e[u] = v;
}
else if(v<=e[u]) insert(l[u],v);
else if(v>e[u]) insert(r[u],v);
}
int cnt[N],max_depth; //cnt记录每层最大节点数量
void dfs(int u,int depth)
{
if(u==0) return;
cnt[depth]++;
max_depth = max(max_depth,depth);
dfs(l[u],depth+1);
dfs(r[u],depth+1);
}
int main()
{
cin >> n;
int root = 0;
for(int i=0;i<n;i++)
{
int w;
cin >> w;
insert(root,w);
}
dfs(root,0); //根节点是第0层(防止空树情况所以用第0层)
int a = cnt[max_depth],b = cnt[max_depth-1];
printf("%d + %d = %d",a,b,a+b);
return 0;
}
PAT1119 前序遍历 + 后序遍历 输出可能二叉树
中序遍历+ 后序遍历 或者 前序遍历 确定唯一二叉树
后序遍历 + 前序遍历 不能唯一确定一个二叉树
就是如果给了你前序遍历和后序遍历
以前序遍历为例,你只知道第一个节点是根节点,你不知道左子树和右子树的长度
这个题节点数比较小,所以可以暴搜枚举左子树长度,来找到一个合适的二叉树构造
#include <iostream>
#include <string>
using namespace std;
const int N = 40;
int n;
int pre[N],post[N];
//是前序和后序树的包含的节点的位置序列
int dfs(int prel,int prer,int postl,int postr,string & scheme)
{
//递归边界
if(prel > prer) return 1; //达到这个条件说明这个方案是成功的,因为我们子树长度是由prer,prel定的
//当前子树构建成功
if(pre[prel] != post[postr]) return 0; //如果当前前序根节点根节点与后序根节点不同,则方案不成功
int cnt = 0; //代表当前方案
//下面枚举左子树包含的节点数量
for(int i=prel;i<=prer;i++) //i从prel开始是左子树需要从空开始,不可以从prel+1开始这样是左子树从长度为1开始了
{
string scheme_l,scheme_r; //记录左右子树中序方案
int lcnt = dfs(prel+1,i,postl,postl+i-prel-1,scheme_l);
int rcnt = dfs(i+1,prer,postl+i-prel,postr-1,scheme_r);
if(lcnt && rcnt)
{
scheme = scheme_l + to_string(pre[prel]) + " " + scheme_r;
cnt += lcnt * rcnt;
if(cnt > 1) break;
}
}
return cnt;
}
int main()
{
cin >> n;
for(int i=0;i<n;i++) cin >>pre[i];
for(int i=0;i<n;i++) cin >>post[i];
string scheme; //记录中序遍历结果
int cnt;
cnt = dfs(0,n-1,0,n-1,scheme); //dfs返回构造的树的数量
if(cnt > 1) puts("No");
else puts("Yes");
scheme.pop_back();
cout << scheme;
return 0;
}
Z字型遍历二叉树
思想就是按照层序遍历来遍历二叉树,然后每一层单独存储遍历结果,根据是奇数层还是偶数层翻转对应层序序列即可
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int N = 40;
unordered_map<int,int> l,r,pos;
int postorder[N],inorder[N];
int dfs(int in_l,int in_r,int post_l,int post_r)
{
int root = postorder[post_r];
if(in_l < pos[root]) l[root] = dfs(in_l,pos[root]-1,post_l,post_l+pos[root]-1-in_l);
if(in_r > pos[root]) r[root] = dfs(pos[root]+1,in_r,post_l+pos[root]-in_l,post_r-1);
return root;
}
int main()
{
int n;
cin >> n;
for(int i=0;i<n;i++)
{
cin >> inorder[i];
pos[inorder[i]] = i;
}
for(int i=0;i<n;i++) cin >> postorder[i];
int root = postorder[n-1];
dfs(0,n-1,0,n-1);
int q[N];
int hh = 0,tt = -1;
q[hh] = root;
tt++;
int level = 0;
while(hh<=tt)
{
int head = hh,tail = tt;
while(hh <= tail)
{
int now_node = q[hh++];
if(l[now_node]!=0) q[++tt] = l[now_node];
if(r[now_node]!=0) q[++tt] = r[now_node];
}
level++;
if(level % 2) reverse(q+head,q+tail+1);
}
for(int i=0;i<n;i++) cout << q[i] <<" ";
return 0;
}