1.已知后序和中序,求层序输出——利用两序建树,后bfs输出树上结点值
例题 PAT甲 1020 Tree Traversals (25 分)
https://pintia.cn/problem-sets/994805342720868352/problems/994805485033603072
int pos[35],in[35];
node* root=build(0,n-1,0,n-1);
bfs(root);
struct node {
int val;
node *left,*right;
} tr[35]
node* build(int posL,int posR,int inL,int inR) {
if(posL>posR) return NULL;
node* r=(struct node*)malloc(sizeof (struct node*));
r->val=pos[posR];
int k=inL;
while(pos[posR]!=in[k]) k++;
int numLeft=k-inL;
r->left=build(posL,posL+numLeft-1,inL,k-1);
r->right=build(posL+numLeft,posR-1,k+1,inR);
return r; //!!!
}
void bfs(node* r) {
int cnt=0;
queue<node*> q;
q.push(r);
while(!q.empty()) {
node* p=q.front();
q.pop();
cout<<p->val;
cnt++;
if(cnt<n) cout<<" ";
if(p->left!=NULL) q.push(p->left);
if(p->right!=NULL) q.push(p->right);
}
}
2.已知前序和中序(进栈前序,出栈中序),输出后序遍历——建树,递归后序遍历
例题:1086 Tree Traversals Again (25 分)
https://pintia.cn/problem-sets/994805342720868352/problems/994805380754817024
vector<int>pre,in;
stack<int> st;
node* r=build(0,n-1,0,n-1);
post(r);
node* build(int preL,int preR,int inL,int inR){
if(preL>preR) return NULL;
node* r=(struct node*)malloc(sizeof(struct node));
r->val=pre[preL];
int k=inL;
while(pre[preL]!=in[k]) k++;
int numLeft=k-inL;
r->left=build(preL+1,preL+numLeft,inL,k-1);
r->right=build(preL+numLeft+1,preR,k+1,inR);
return r;
}
void post(node* r){
if(r==NULL) return;
post(r->left);
post(r->right);
cout<<r->val;
cnt++;
if(cnt<n) cout<<' ';
}
不建树版的
post(0,0,n-1);
void post(int root,int inL,int inR){
if(inL>inR) return;
int k=inL;
while(pre[root]!=in[k]) k++;
int numLeft=k-inL;
post(root+1,inL,k-1);
post(root+numLeft+1,k+1,inR);
cout<<pre[root];
cnt++;
if(cnt<n) cout<<" ";
}
已知前序中序,输出后序第一个元素。
#include<bits/stdc++.h>
using namespace std;
int pre[50005],in[50005];
int flag;
void PostOrder(int prel,int inl,int inr){
if(inl>inr||flag==1) return;
int i=inl;
while(pre[prel]!=in[i]) i++;
PostOrder(prel+1,inl,i-1);
PostOrder(prel+i-inl+1,i+1,inr);
if(flag==0){
printf("%d",in[i]);
flag=1;
}
}
int main(){
int n;
scanf("%d",&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,0,n-1);
return 0;
}
/*
0 1 2 3 4 5 6
prel in
prel+1
*/
3.已知前序和后序,问中序是否唯一并打印中序结果(或符合的结果之一)
- 用unique标记是否唯一
- 已知二叉树的前序和后序是无法唯一确定一颗二叉树的,因为一个结点可能是根的左孩子也有可能是根的右孩子
- 如果发现了一个无法确定的状态(if(k-preL>1)),置unique = false
- 题目只需要输出一个方案——假定这个不可确定的孩子的状态是右孩子
- 如何求根结点和左右孩子划分?首先我们需要知道树的表示范围,需要四个变量,分别是前序的开始的地方prel,前序结束的地方prer,后序开始的地方postl,后序结束的地方postr,前序的开始的第一个应该是后序的最后一个是相等的,这个结点就是根结点,以后序的根结点的前面一个结点作为参考,寻找这个结点在前序的位置,就可以根据这个位置来划分左右孩子,递归处理
int pre[35],post[35];
vector<int> in;
int unique=1;
getIn(0,n-1,0,n-1);
void getIn(int preL,int preR,int postL,int postR){
if(preL==preR){
in.push_back(pre[preL]);
return;
}
if(pre[preL]==post[postR]){
int k=preL;
while(pre[k]!=post[postR-1]) k++;
if(k-preL>1)//若不符合条件则该节点可能是右子树根节点,也可能是左子树根节点
getIn(preL+1,k-1,postL,postL+(k-preL-1)-1);
else unique=0;
in.push_back(post[postR]);
getIn(k,preR,postL+(k-preL-1),postR-1);
}
}
4.输入各个节点的子节点,求反转后的层序遍历和中序遍历——入数组,做标记,找根节点,数组下标传参,无需反转只需在遍历时改为先右后左即可
- string s,int num=stoi(s)
- scanf,gets(),cin,cin.get(),cin.getline() 区分 https://mp.csdn.net/postedit/100033286
- 中序递归遍历时判断 if(tree[r].right!=-1) (初始值为-1)
参考例题:1102 Invert a Binary Tree (25 分)
https://pintia.cn/problem-sets/994805342720868352/problems/994805365537882112
cin>>n;
char ch=getchar();
char a,b;
for(int i=0; i<n; i++) {
scanf("%c %c\n",&a,&b); //最后一行ctrl+z才出结果,但完全正确!!! :)
tree[i].val=i;
if(a!='-') {
vis[a-'0']=1;
tree[i].left=a-'0';
}
if(b!='-') {
vis[b-'0']=1;
tree[i].right=b-'0';
}
}
for(int i=0; i<n; i++) { //读入数据,是否含回车,scanf/cin易错!!!
cin>>a>>b;
tree[i].val=i;
if(a[0]!='-') {
vis[stoi(a)]=1;
tree[i].left=stoi(a);
}
if(b[0]!='-') {
vis[stoi(b)]=1;
tree[i].right=stoi(b);
}
}
for(int i=0; i<n; i++) //找根节点
if(vis[i]==0) root=i;
Level(root);
cnt=0;
inOrder(root);
void Level(int root) { //全程无指针,仅需下标入队列
queue<int> q;
q.push(root);
int p;
while(!q.empty()) {
p=q.front();
q.pop();
cout<<tree[p].val;
cnt++;
if(cnt<n) cout<<' ';
if(tree[p].right!=-1) q.push(tree[p].right);
if(tree[p].left!=-1) q.push(tree[p].left);
}
cout<<endl;
}
void inOrder(int r) { //遍历前先判断(初始值为-1)
if(tree[r].right!=-1) inOrder(tree[r].right);
cout<<tree[r].val;
cnt++;
if(cnt<n) cout<<' ';
if(tree[r].left!=-1) inOrder(tree[r].left);
}
5.给出根节点及各节点的子节点情况,求深度最小的叶子结点的深度及个数——bfs,dfs
- bfs,找到最小价格不能直接退出——因为要计最优价格的数量,故继续与后续价格比较
- dfs,赋根节点价格(各节点实时更新)递归边界——到达子节点(两种情况计数)
参考题目 1106. Lowest Price in Supply Chain (25)
struct node {
double p; //该节点价格
vector<int> child; //可能无子节点——目标,或多个子节点
} store[100005];
void bfs() {
queue<int> q;
q.push(0);
store[0].p=p;
int root,s;
while(!q.empty()) {
root=q.front();
q.pop();//(下一行)无子节点——目标,因为要计数,不可马上退出,与最小价格相比cnt有两种情况
if(store[root].child.size()==0) {
if(ans>store[root].p) ans=store[root].p,cnt=1;
else if(store[root].p==ans)cnt++;
} else {
for(int i=0; i<store[root].child.size(); i++) {
s=store[root].child[i];
store[s].p=store[root].p*(1+r); //初始时已做处理r=r/100
//printf("**%.4lf %d\n",store[s].p,s);
q.push(s);
}
}
}
printf("%.4lf %d\n",ans,cnt);
}
--------------------------------------------------------------------------------------
有结构体的dfs
store[0].p=p;
dfs(0);
void dfs(int root) {
if(store[root].child.size()==0){
if(store[root].p<ans) ans=store[root].p,cnt=1;
else if(store[root].p==ans) cnt++;
return;
}
int s;
for(int i=0;i<store[root].child.size();i++){
s=store[root].child[i];
store[s].p=store[root].p*(1+r);
dfs(s);
}
}
--------------------------------------------------------------------------------------
无需结构体的dfs,单参数(下标,深度)
void dfs(int index, int depth) {
if(mindepth < depth)
return ;
if(v[index].size() == 0) {
if(mindepth == depth)
minnum++;
else if(mindepth > depth) {
mindepth = depth;
minnum = 1;
}
}
for(int i = 0; i < v[index].size(); i++)
dfs(v[index][i], depth + 1);
}
6.给出根节点及各节点的子节点情况,求深度最大的叶子结点的深度及个数(同上)
参考题目:1090. Highest Price in Supply Chain (25)
void dfs(int root,int depth){
if(v[root].size()==0){
if(maxdep<depth) maxdep=depth,cnt=1;
else if(maxdep==depth) cnt++;
return;
}
for(int i=0;i<v[root].size();i++)
dfs(v[root][i],depth+1);
}
7.给出根节点及各节点的子节点情况,求所有叶子结点的总值(数量x价格x涨价百分比)(同上)
参考题目:1079 Total Sales of Supply Chain (25 分)
注意:若该处为叶子结点,则该值为0,其后输入其销售数量
vector<int>v[100005];
int amount[100005];
void dfs(int root,int depth){
if(v[root].size()==0){
ans+=p*pow((1+r),depth)*amount[root];
return;
}
for(int i=0;i<v[root].size();i++)
dfs(v[root][i],depth+1);
}
-----------------------------------------------
注意若该处为叶子结点,则该值为0,其后输入其销售数量
if(t==0){
cin>>num;
amount[i]=num;
continue;
}
8.输入各节点情况,求每一层叶结点个数
参考题目:1004 Counting Leaves (30 分)
https://pintia.cn/problem-sets/994805342720868352/problems/994805521431773184
注意:
- maxd记录叶结点所在最大层
- d++的条件
int depth[105]= {0};
vector<int> v[105];
dfs(1,1);
void dfs(int idx,int d) {
if(v[idx].size()==0) {
depth[d]++; //是叶子节点d++
maxd=max(maxd,d); //确定最后最大叶子结点所在层数
return;
}
for(int i=0; i<v[idx].size(); i++)
dfs(v[idx][i],d+1);
}
-------------------------------------------------------------------
二维向量清空操作
for(int i=0; i<105; i++)
v[i].clear();
9.给出各结点子结点情况,求节点数最多的层数及其节点个数——只求数目,故dfs每遍历到一个点就将该层level【i】++,找最大level【i】(遍历就对了)
参考题目:1094 The Largest Generation (25 分)
void dfs(int idx,int depth) {
level[depth]++;
if(v[idx].size()==0) return;
for(int i=0; i<v[idx].size(); i++)
dfs(v[idx][i],depth+1);
}
10.根据输入顺序建二叉搜索树,输出最深的两层的结点之和的算式——建树,存depth
注意<=均为左子树
参考题目:1115 Counting Nodes in a BST (30 分)
https://pintia.cn/problem-sets/994805342720868352/problems/994805355987451904
struct node{ //建树
int val;
node *left,*right;
}tr[1005];
int depth[15]={0}; //各深度节点数目纪录
int maxd=-1; //最大深度记录
node* root=NULL;
for(int i=0;i<n;i++){
cin>>x;
root=build(root,x); //Fixed format
}
dfs(root,1);
node* build(node* root,int x){
if(root==NULL){
root=(struct node*)malloc(sizeof(struct node));
root->val=x;
root->left=root->right=NULL;
return root;
}
if(x<=root->val) root->left=build(root->left,x);
else if(x>root->val)root->right=build(root->right,x);
return root;
}
void dfs(node* root,int d){
depth[d]++;
maxd=max(maxd,d);
if(root->left==NULL&&root->right==NULL) return;
if(root->left!=NULL) dfs(root->left,d+1);
if(root->right!=NULL) dfs(root->right,d+1);
}
11.给出各节点序号及其值、总W,按从大到小的顺序打印 满足总值为W的分支(末端必须为到叶节点)的各个节点的值的序列
参考题目:1053 Path of Equal Weight (30 分)
https://pintia.cn/problem-sets/994805342720868352/problems/994805424153280512
各节点入vector后排序+dfs
int ans[100005];
struct node {
int val;
vector<int> v;
} tr[105];
bool cmp(int a,int b) { //存进去的是下标,比较的依据为下标所对应的值的大小
return tr[a].val>tr[b].val;
}
ans[0]=tr[0].val; //根节点必在
dfs(0,tr[0].val,1);
void dfs(int id,int totalW,int pos) {
if(totalW==W) {
if(tr[id].v.size()==0) { //末尾必须为叶子结点
for(int i=0; i<pos; i++) {
if(i) cout<<' ';
cout<<ans[i];
}
cout<<endl;
}
return;
}
node now=tr[id];
for(int i=0; i<now.v.size(); i++) {
if(totalW+tr[now.v[i]].val<=W) {
ans[pos]=tr[now.v[i]].val; //如果符合要求该点值计入ans【】
dfs(now.v[i],totalW+tr[now.v[i]].val,pos+1);
}
}
}