9.2 二叉树的遍历
【例】A1020 Tree Traversals (25 分)
后序 + 中序 -> 层序输出
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
int n,post[40],in[40];
struct node{
int data;
node* lChild,*rChild;
node(){lChild=rChild=NULL;}
};
vector<node*> ans;
node* create(int poL,int poR,int inL,int inR)
{
if(poL>poR) return NULL;
node* root=new node; // #include <iostream> + using namespace std;
root->data=post[poR];
int k;
for(k=inL;k<=inR;k++)
if(in[k]==post[poR])
break;
int numLeft=k-inL;
root->lChild=create(poL,poL+numLeft-1,inL,k-1);
root->rChild=create(poL+numLeft,poR-1,k+1,inR);
return root;
}
void layerOrder(node* root)
{
queue<node*> q;
q.push(root);
while(!q.empty())
{
node* front=q.front();
q.pop();
ans.push_back(front);
if(front->lChild!=NULL)
q.push(front->lChild);
if(front->rChild!=NULL)
q.push(front->rChild);
}
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&post[i]);
for(int i=0;i<n;i++)
scanf("%d",&in[i]);
node* root=create(0,n-1,0,n-1);
layerOrder(root);
for(int i=0;i<n;i++)
{
if(i==0) printf("%d",ans[i]->data);
else printf(" %d",ans[i]->data);
}
return 0;
}
【例】A1086 Tree Traversals Again (25 分)
ATTENTION
- pop的顺序是中序遍历
- push的顺序是先序遍历
- 所以就是已知先序遍历个中序遍历,求后序遍历!
#include <iostream>
#include <string>
#include <stack>
using namespace std;
int n,pre[40],in[40];
struct node{
int data;
node* lChild,*rChild;
};
node* create(int preL,int preR,int inL,int inR)
{
if(preL>preR) return NULL;
node* root=new node;
root->data=pre[preL];
int k;
for(k=1;k<=n;k++)
if(pre[preL]==in[k])
break;
int numLeft=k-inL;
root->lChild=create(preL+1,preL+numLeft,inL,k-1);
root->rChild=create(preL+numLeft+1,preR,k+1,inR);
return root;
}
void postOrder(node* root)
{
if(root==NULL) return;
postOrder(root->lChild);
postOrder(root->rChild);
if(root->data==pre[1]) cout<<root->data;
else cout<<root->data<<" ";
}
int main()
{
cin>>n;
int d,preIdx=1,inIdx=1;
stack<int> st;
for(int i=1;i<=2*n;i++)
{
string op;
cin>>op;
if(op=="Push")
{
cin>>d;
st.push(d);
pre[preIdx++]=d;
}
else
{
d=st.top();
st.pop();
in[inIdx++]=d;
}
}
//1-n pre in -> post
node* root=NULL;
root=create(1,n,1,n);
postOrder(root);
}
直接模拟
ATTENTION
- push的时候,记录push的前一个结点,那一定是它的父亲。
- pop的时候,先记录top(),再pop,因为pop的一定是父亲结点。直到下一个push,此时push的一定是top()的孩子。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct Node{
int lChild,rChild;
Node(){lChild=rChild=-1;}
}node[40];
int n,root;
vector<int> seq;
void postOrder(int r)
{
if(r==-1) return;
postOrder(node[r].lChild);
postOrder(node[r].rChild);
if(root==r) cout<<r;
else cout<<r<<" ";
}
int main()
{
cin>>n;
string op;
int d,father=-1;
for(int i=1;i<=2*n;i++)
{
cin>>op;
if(op=="Push")
{
cin>>d;
if(i==1) root=d;
seq.push_back(d);
if(father!=-1)
{
if(node[father].lChild==-1)
node[father].lChild=d;
else
node[father].rChild=d;
}
father=d;
}
else
{
father=seq[seq.size()-1];
seq.pop_back();
}
}
postOrder(root);
return 0;
}
【例】A1102 Invert a Binary Tree (25 分)
invert vt. 使…转化;使…颠倒;使…反转;使…前后倒置
ATTENTION
- 反转全靠样例猜orz
- 需要自己找根节点,这里在数组
pre[]
中记录了每个结点的父亲,这样随便选一个有父亲的结点向上递归到没有父亲的结点,就是根了。笔记中的方法是,不是任何结点的孩子的结点就是根。 - \: \: string只能被cin读入 \: \:
- \: \: 反转的正确做法:在后序遍历中交换左右孩子! \:
void postOrder(int root)
{
if(root==-1)
return;
postOrder(node[root].lChild);
postOrder(node[root].rChild);
swap(node[root].lChild,node[root].rChild) //头文件 #include<iostream>
}
#include <cstdio>
#include <iostream>
#include <queue>
#include <string>
#include <algorithm>
using namespace std;
int n,root,idx=1;
int pre[20];
int node[20][2];
void levelOrder(int t)
{
queue<int> q;
q.push(t);
int cnt=1;
while(!q.empty())
{
int f=q.front();
if(cnt==1)
printf("%d",f);
else
printf(" %d",f);
cnt++;
q.pop();
if(node[f][1]!=-1) q.push(node[f][1]);
if(node[f][0]!=-1) q.push(node[f][0]);
}
printf("\n");
}
int findRoot(int t)
{
if(pre[t]==-1)
return t;
findRoot(pre[t]);
}
void inOrder(int t)
{
if(node[t][1]!=-1)
inOrder(node[t][1]);
if(idx==1)
printf("%d",t);
else
printf(" %d",t);
idx++;
if(node[t][0]!=-1)
inOrder(node[t][0]);
}
int main()
{
scanf("%d",&n);
fill(pre,pre+n,-1);
for(int i=0;i<n;i++)
{
string s1,s2;
// scanf("%s %s",s1,s2);
cin>>s1>>s2;
//printf("*%s %s*",s1.c_str(),s2.c_str());
if(s1[0]!='-')
{
node[i][0]=(int)(s1[0]-'0');
pre[s1[0]-'0']=i;
}
else
node[i][0]=-1;
if(s2[0]!='-')
{
node[i][1]=(int)(s2[0]-'0');
pre[s2[0]-'0']=i;
}
else
node[i][1]=-1;
}
for(int i=0;i<n;i++)
{
if(pre[i]!=-1)
root=findRoot(i);
}
levelOrder(root);
inOrder(root);
return 0;
}
【例】*A1151 LCA in a Binary Tree (30 分)
ATTENTION
- 不建树,建树疯狂超时。利用先序和中序遍历,判断结点u和v相对于根的位置。如果两者分别在左右子树,则这时候的根为最近公共祖先。如果u和v在同一子树中,则只递归该部分子树,继续寻找最近公共祖先。
- 这道题有几个点特别卡时间。法1是取巧过的,由于结点的
key
是int
范围的,多试了几次试出了范围… - 理论上讲,
pos
应该用map
实现。但只用map也会超时,需要在多个地方注意减少操作。
#include <cstdio>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
int m,n,u,v,pre[10010],in[10010];
map<int,int> mp,pos;
void create(int preL,int inL,int inR)
{
if(inL>inR) return ;
int a=pos[u],b=pos[v];
int k=pos[pre[preL]];
if(a==k)
{
printf("%d is an ancestor of %d.\n",u,v);
return ;
}
if(b==k)
{
printf("%d is an ancestor of %d.\n",v,u);
return ;
}
if((a<k&&b>k)||(a>k&&b<k))
{
printf("LCA of %d and %d is %d.\n",u,v,pre[preL]);
return;
}
if(a<k&&b<k)
create(preL+1,inL,k-1);
if(a>k&&b>k)
create(preL+k-inL+1,k+1,inR);
}
int main()
{
scanf("%d %d",&m,&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&in[i]);
pos[in[i]]=i;
}
for(int i=1;i<=n;i++)
scanf("%d",&pre[i]);
for(int i=0;i<m;i++)
{
scanf("%d %d",&u,&v);
if(pos[u]==0&&pos[v]==0)
printf("ERROR: %d and %d are not found.\n",u,v);
else if(pos[u]==0)
printf("ERROR: %d is not found.\n",u);
else if(pos[v]==0)
printf("ERROR: %d is not found.\n",v);
else
create(1,1,n);
}
return 0;
}
取巧版本
//取巧
#include <cstdio>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
int m,n,u,v,pre[10010],in[10010],pos[1000010];
map<int,int> mp;
void create(int preL,int preR,int inL,int inR)
{
if(preL>preR) return ;
int k=pos[pre[preL]];
int numLeft=k-inL;
if(pos[u]==k)
{
printf("%d is an ancestor of %d.\n",u,v);
return ;
}
if(pos[v]==k)
{
printf("%d is an ancestor of %d.\n",v,u);
return ;
}
if((pos[u]<k&&pos[v]>k)||(pos[u]>k&&pos[v]<k))
{
printf("LCA of %d and %d is %d.\n",u,v,pre[preL]);
return;
}
if(pos[u]<k&&pos[v]<k)
create(preL+1,preL+numLeft,inL,k-1);
if(pos[u]>k&&pos[v]>k)
create(preL+numLeft+1,preR,k+1,inR);
}
int main()
{
scanf("%d %d",&m,&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&in[i]);
pos[in[i]]=i;
mp[in[i]]=1;
}
for(int i=1;i<=n;i++)
scanf("%d",&pre[i]);
for(int i=0;i<m;i++)
{
scanf("%d %d",&u,&v);
if(mp[u]==0&&mp[v]==0)
printf("ERROR: %d and %d are not found.\n",u,v);
else if(mp[u]==0)
printf("ERROR: %d is not found.\n",u);
else if(mp[v]==0)
printf("ERROR: %d is not found.\n",v);
else
create(1,n,1,n);
}
return 0;
}
【例】A1127 ZigZagging on a Tree (30 分)
ATTENTION
- 每次这种遍历判断,当前与前一个比较再输出的时候,都要记得在循环结束后,需要输出最后一组!!!
#include <cstdio>
int n,m;
int de[510]={0};
int g[510][510]={0};
bool vis[510]={false};
void dfs(int u)
{
vis[u]=true;
for(int v=1;v<=n;v++)
{
if(vis[v]==false&&g[u][v]==1)
dfs(v);
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++)
{
int c1,c2;
scanf("%d %d",&c1,&c2);
de[c1]++;
de[c2]++;
g[c1][c2]=g[c2][c1]=1;
}
int even=0,odd=0;
for(int i=1;i<=n;i++)
{
if(de[i]%2==0) even++;
else odd++;
if(i==1) printf("%d",de[i]);
else printf(" %d",de[i]);
}
bool flag=true;
dfs(1);
for(int i=1;i<=n;i++)
{
if(vis[i]==false)
{
flag=false;
break;
}
}
if(flag&&even==n) printf("\nEulerian");
else if(flag&&even==n-2&&odd==2) printf("\nSemi-Eulerian");
else printf("\nNon-Eulerian");
return 0;
}
【例】A1110 Complete Binary Tree (25 分)
ATTENTION
- 笑死。这道题debug了好久。第二天才发现不能用
char
存储结点序号,结点可能是两位数。白瞎9分。
#include <cstdio>
#include <string>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
struct Node{
int lChild,rChild,idx;
Node(){lChild=rChild=-1;}
}node[100];
int n,layer[100],cnt=1;
bool vis[100]={false};
bool judge[100]={false};
void layerOrder(int root)
{
queue<int> q;
q.push(root);
fill(layer,layer+100,-1);
node[root].idx=1;
while(!q.empty())
{
int u=q.front();
layer[cnt++]=u;
q.pop();
judge[node[u].idx]=true;
if(node[u].lChild!=-1)
{
q.push(node[u].lChild);
node[node[u].lChild].idx=node[u].idx*2;
}
if(node[u].rChild!=-1)
{
q.push(node[u].rChild);
node[node[u].rChild].idx=node[u].idx*2+1;
}
}
}
int main()
{
scanf("%d",&n);
if(n==0)
{
printf("NO 0");
return 0;
}
for(int i=0;i<n;i++)
{
//getchar(); //。。。。。。
//char l,r;
string l,r;
cin>>l>>r;
// scanf("%c %c",&l,&r);
if(l!="-")
{
int tmp=atoi(l.c_str());
node[i].lChild=tmp;
vis[tmp]=true;
}
if(r!="-")
{
int tmp=atoi(r.c_str());
node[i].rChild=tmp;
vis[tmp]=true;
}
}
int root=0;
while(vis[root]==true) root++;
layerOrder(root);
for(int i=1;i<=n;i++)
{
if(judge[i]==false)
{
printf("NO %d\n",root);
return 0;
}
}
printf("YES %d\n",layer[n]);
return 0;
}
9.3 树的遍历
【例】A1004 Counting Leaves (30 分)
hierarchy n. 层级;等级制度
pedigree n.家谱
fix v.固定
For the sake of simplicity 简单起见
#include <cstdio>
#include <vector>
#include <queue>
using namespace std; //使用STL要加这句话
const int maxn=200;
int n,m;
int layer[maxn]={};
int maxLayer=-1;
struct node{
int layer;
vector<int> child;
node()
{
layer=-1;
}
}tNode[maxn];
void layerOrder(int root)
{
queue<int> Q;
Q.push(root);
tNode[root].layer=0;
maxLayer=0;
while(!Q.empty())
{
int front=Q.front();
Q.pop();
if(tNode[front].child.size()==0)
layer[tNode[front].layer]+=1;
for(int i=0;i<tNode[front].child.size();i++)
{
int child=tNode[front].child[i];
Q.push(child);
tNode[child].layer=tNode[front].layer+1;
if(tNode[child].layer>maxLayer)
maxLayer=tNode[child].layer;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
int id,k;
scanf("%d%d",&id,&k);
for(int j=0;j<k;j++)
{
int cidx;
scanf("%d",&cidx);
tNode[id].child.push_back(cidx);
}
}
layerOrder(1);
for(int i=0;i<=maxLayer;i++)
{
printf("%d",layer[i]);
if(i!=maxLayer)
printf(" ");
}
return 0;
}
【例】A1053 Path of Equal Weight (30 分)
ATTENTION
vector
默认的大小比较方式与题目中的类似,所以直接比较vector
即可。但在不知道这一点前,vector
的比较很是麻烦。- 一开始放在
ans
中的是结点的下标,所以比较大小一直有问题orz。 - 这道题还可以用DFS+剪枝的思想做。这样可以免去最后的比较大小。
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
int n,m,s,k;
struct Node{
int w;
vector<int> child;
}node[110];
vector<int> path;
vector<vector<int> > ans;
bool cmp(vector<int> a,vector<int> b)
{
return a>b;
}
void dfs(int root)
{
path.push_back(node[root].w);
if(node[root].child.size()==0) //叶子结点
{
int sum=0;
for(int i=0;i<path.size();i++)
sum+=path[i];
if(sum==s)
ans.push_back(path);
//path.pop_back();
return;
}
for(int i=0;i<node[root].child.size();i++)
{
dfs(node[root].child[i]);
path.pop_back();
}
}
int main()
{
scanf("%d %d %d",&n,&m,&s);
for(int i=0;i<n;i++)
scanf("%d",&node[i].w);
for(int i=0;i<m;i++)
{
int v,tmp;
scanf("%d %d",&v,&k);
for(int j=0;j<k;j++)
{
scanf("%d",&tmp);
node[v].child.push_back(tmp);
}
}
dfs(0);
sort(ans.begin(),ans.end(),cmp);
for(int i=0;i<ans.size();i++)
{
for(int j=0;j<ans[i].size();j++)
{
if(j==0) printf("%d",ans[i][j]);
else printf(" %d",ans[i][j]);
}
printf("\n");
}
return 0;
}
【例】A1094 The Largest Generation (25 分)
ATTENTION
- 因为根节点的层数为1而非0,所以输出的层号的初始值应该为1,而不是0。
- DFS求解层号
void dfs(int idx,int level)
{
cnt[level]++;
for(int i=0;i<node[idx].size();i++)
{
dfs(node[idx][i],level+1);
}
}
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;
int n,m,k,maxLevel=0;
vector<int> node[110];
int level[110]={0},cnt[110]={0};
void levelOrder()
{
queue<int> q;
q.push(1);
while(!q.empty())
{
int cur=q.front();
q.pop();
for(int i=0;i<node[cur].size();i++)
{
level[node[cur][i]]=level[cur]+1;
cnt[level[cur]+1]++;
q.push(node[cur][i]);
maxLevel=max(maxLevel,level[node[cur][i]]); //头文件?
}
}
}
int main()
{
level[1]=1;cnt[1]=1;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
{
int idx;
scanf("%d %d",&idx,&k);
for(int j=1;j<=k;j++)
{
int tmp;
scanf("%d",&tmp);
node[idx].push_back(tmp);
}
}
levelOrder();
int idx=1,max=1; //如果只有root 答案是 0 1
for(int i=1;i<=maxLevel;i++)
{
if(max<cnt[i])
{
idx=i;
max=cnt[i];
}
}
printf("%d %d",max,idx);
return 0;
}
【例】A1090 Highest Price in Supply Chain (25 分)
ATTENTION
- 又是死在英语上的一天呢,棒棒的 😃
- 一棵树,求树高和最高层叶子数量。
#include <cstdio>
#include <vector>
using namespace std;
int n,lev[100010]={0},maxLev=0;;
double p,r;
vector<int> node[100010];
void dfs(int t,int l)
{
lev[t]=l;
if(maxLev<l) maxLev=l;
for(int i=0;i<node[t].size();i++)
{
dfs(node[t][i],l+1);
}
}
int main()
{
scanf("%d %lf %lf",&n,&p,&r);
for(int i=0;i<n;i++) //所有结点序号+1
{
int tmp;
scanf("%d",&tmp);
node[tmp+1].push_back(i+1);
}
dfs(0,0);
for(int i=1;i<maxLev;i++)
{
p=(1+0.01*r)*p;
}
printf("%.2f ",p);
int cnt=0;
for(int i=0;i<=n;i++)
{
if(lev[i]==maxLev)
cnt++;
}
printf("%d",cnt);
return 0;
}
9.4 二叉查找树BST
【例】A1043 Is It a Binary Search Tree (25 分)
ATTENTION
- 这道题的关键在于:按先序遍历 (或后序遍历) 的顺序插入可以得到一棵唯一地二叉搜索树。因为二叉查找树左<根<右,所以在插入时,根据二叉查找树的有序性,可以自动地区分左子树和右子树,从而得到一棵唯一地二叉树。
- 怎么回事儿,这个YES和NO竟然是全大写的,爆零太可怕了orz
- 递归函数!复制粘贴修改时!一定要好好修改!别漏掉任何一个地方的递归!!!
#include <cstdio>
#include <vector>
using namespace std;
int n;
vector<int> origin,mir;
struct node{
int data;
node* lChild,*rChild;
node(int x)
{
lChild=rChild=NULL;
data=x;
}
node()
{
lChild=rChild=NULL;
}
};
void insert(node* &root,int x)
{
if(root==NULL)
{
root=new node(x);
return ;
}
if(x<root->data)
insert(root->lChild,x);
else
insert(root->rChild,x);
}
void mirInsert(node* &root,int x)
{
if(root==NULL)
{
root=new node(x);
return ;
}
if(x>=root->data)
mirInsert(root->lChild,x);
else
mirInsert(root->rChild,x);
}
void preOrder(node* root,vector<int> &path)
{
if(root==NULL) return ;
path.push_back(root->data);
preOrder(root->lChild,path);
preOrder(root->rChild,path);
}
void postOrder(node* root,vector<int> &path)
{
if(root==NULL) return;
postOrder(root->lChild,path);
postOrder(root->rChild,path);
path.push_back(root->data);
}
int main()
{
node* root=NULL,*mirRoot=NULL;
scanf("%d",&n);
vector<int> num(n);
for(int i=0;i<n;i++)
{
scanf("%d",&num[i]);
insert(root,num[i]);
mirInsert(mirRoot,num[i]);
}
preOrder(root,origin);
preOrder(mirRoot,mir);
if(num==origin)
{
printf("YES\n");
origin.clear();
postOrder(root,origin);
printf("%d",origin[0]);
for(int i=1;i<n;i++)
printf(" %d",origin[i]);
return 0;
}
if(num==mir)
{
printf("YES\n");
mir.clear();
postOrder(mirRoot,mir);
printf("%d",mir[0]);
for(int i=1;i<n;i++)
printf(" %d",mir[i]);
}
else
printf("NO\n");
return 0;
}
#include <cstdio>
#include <vector>
using namespace std;
int n;
vector<int> origin,mir;
struct node{
int data;
node* lChild,*rChild;
node(int x)
{
lChild=rChild=NULL;
data=x;
}
node()
{
lChild=rChild=NULL;
}
};
void insert(node* &root,int x)
{
if(root==NULL)
{
root=new node(x);
return ;
}
if(x<root->data)
insert(root->lChild,x);
else
insert(root->rChild,x);
}
void mirInsert(node* &root,int x)
{
if(root==NULL)
{
root=new node(x);
return ;
}
if(x>=root->data)
mirInsert(root->lChild,x);
else
mirInsert(root->rChild,x);
}
void preOrder(node* root,vector<int> &path)
{
if(root==NULL) return ;
path.push_back(root->data);
preOrder(root->lChild,path);
preOrder(root->rChild,path);
}
void mirpreOrder(node* root,vector<int> &path)
{
if(root==NULL) return ;
path.push_back(root->data);
mirpreOrder(root->rChild,path);
mirpreOrder(root->lChild,path);
}
void postOrder(node* root,vector<int> &path)
{
if(root==NULL) return;
postOrder(root->lChild,path);
postOrder(root->rChild,path);
path.push_back(root->data);
}
void mirpostOrder(node* root,vector<int> &path)
{
if(root==NULL) return;
mirpostOrder(root->rChild,path);
mirpostOrder(root->lChild,path);
path.push_back(root->data);
}
int main()
{
node* root=NULL,*mirRoot=NULL;
scanf("%d",&n);
vector<int> num(n);
for(int i=0;i<n;i++)
{
scanf("%d",&num[i]);
insert(root,num[i]);
//mirInsert(mirRoot,num[i]);
}
preOrder(root,origin);
mirpreOrder(root,mir);
if(num==origin)
{
printf("YES\n");
origin.clear();
postOrder(root,origin);
printf("%d",origin[0]);
for(int i=1;i<n;i++)
printf(" %d",origin[i]);
return 0;
}
if(num==mir)
{
printf("YES\n");
mir.clear();
mirpostOrder(root,mir);
printf("%d",mir[0]);
for(int i=1;i<n;i++)
printf(" %d",mir[i]);
}
else
printf("NO\n");
return 0;
}
【例】A1064 Complete Binary Search Tree (30 分)
ATTENTION
- 二叉搜索树的中序遍历是有序的。
- 以数组存储完全二叉树,其数组本身就是该完全二叉树的层次遍历。
#include <cstdio>
#include <algorithm>
using namespace std;
int n,idx=1;
int num[1010],cbt[1010];
void inOrder(int root) //中序遍历的是cbt数组的下标
{
if(root>n) return ;
inOrder(root*2);
cbt[root]=num[idx++];
inOrder(root*2+1);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
sort(num+1,num+1+n);
inOrder(1);
printf("%d",cbt[1]);
for(int i=2;i<=n;i++)
printf(" %d",cbt[i]);
return 0;
}
【例】A1099 Build A Binary Search Tree (30 分)
property n.属性
traversal n.遍历
#include <cstdio>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
struct Node{
int left,right;
Node()
{
left=-1;right=-1;
}
}node[110];
vector<int> num,turn; //输入的数,前序遍历
int ans[110];
int n;
void preOrder(int t)
{
if(node[t].left!=-1)
preOrder(node[t].left);
turn.push_back(t);
if(node[t].right!=-1)
preOrder(node[t].right);
}
void levelOrder(int t)
{
queue<int> q;
q.push(0);
int idx=0;
while(!q.empty())
{
int cur=q.front();
q.pop();
printf("%d",ans[cur]);
if(idx!=n-1)
printf(" ");
idx++;
if(node[cur].left!=-1)
q.push(node[cur].left);
if(node[cur].right!=-1)
q.push(node[cur].right);
}
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
int l,r;
scanf("%d %d",&l,&r);
node[i].left=l;
node[i].right=r;
}
for(int i=0;i<n;i++)
{
int tmp;
scanf("%d",&tmp);
num.push_back(tmp);
}
sort(num.begin(),num.end());
preOrder(0);
for(int i=0;i<n;i++)
ans[turn[i]]=num[i];
levelOrder(0);
return 0;
}
9.5 平衡二叉树AVL
【例】A1066 Root of AVL Tree (25 分)
ATTENTION
- AVL的插入有相当强的对称性,根据对称性能比较快地记住模板。
#include <cstdio>
#include <algorithm>
using namespace std;
int n,data[30];
struct node{
int data,height;
node* lChild,*rChild;
node(int x)
{
lChild=rChild=NULL;
data=x;
height=1;
}
node()
{
lChild=rChild=NULL;
height=1;
}
};
int getHeight(node* root)
{
if(root==NULL) return 0;
return root->height;
}
void updateHeight(node* root)
{
root->height=max(getHeight(root->lChild),getHeight(root->rChild))+1;
}
int getBalanceFactor(node* root)
{
return getHeight(root->lChild)-getHeight(root->rChild);
}
void L(node* &root)
{
node* tmp=root->rChild;
root->rChild=tmp->lChild;
tmp->lChild=root;
updateHeight(root);
updateHeight(tmp);
root=tmp;
}
void R(node* &root)
{
node* tmp=root->lChild;
root->lChild=tmp->rChild;
tmp->rChild=root;
updateHeight(root);
updateHeight(tmp);
root=tmp;
}
void insert(node* &root,int x)
{
if(root==NULL)
{
root=new node(x);
return ;
}
if(x<root->data)
{
insert(root->lChild,x);
updateHeight(root);
if(getBalanceFactor(root)==2)
{
if(getBalanceFactor(root->lChild)==1)
{
R(root);
}
else if(getBalanceFactor(root->lChild)==-1)
{
L(root->lChild);
R(root);
}
}
}
else
{
insert(root->rChild,x);
updateHeight(root);
if(getBalanceFactor(root)==-2)
{
if(getBalanceFactor(root->rChild)==-1)
{
L(root);
}
else if(getBalanceFactor(root->rChild)==1)
{
R(root->rChild);
L(root);
}
}
}
}
int main()
{
scanf("%d",&n);
node* root=NULL;
for(int i=0;i<n;i++)
{
scanf("%d",&data[i]);
insert(root,data[i]);
}
printf("%d",root->data);
}
9.6 并查集
【例】A1107 Social Clusters (30 分)
cluster n.群;簇;集群
ATTENTION
- 这道题的逻辑是:人 – hobby – 群。所以群实际上是hobby的群,所以,把目标放在合并hobby上。
- 假设第一个提出
hobby[tmp]
的人是这个hobby
的根结点。 - 对于每个人的
hobby list
,将这个人i
与list
中的每个hobby
的集合进行合并,即合并同一个群的hobby
集合。 - 对于每个集群的人数,遍历每个人,将其根结点++即可。
- 不愧是30分的题,还是有思考难度的,记得二刷。
#include <cstdio>
#include <algorithm>
using namespace std;
int n,k;
int father[1010],hobby[1010]={0},isRoot[1010]={0};
int findFather(int x)
{
if(father[x]==x)
return x;
return findFather(father[x]);
}
void Union(int a,int b)
{
int faA=findFather(a);
int faB=findFather(b);
if(faA!=faB)
father[faA]=faB;
}
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
father[i]=i; //初始化
scanf("%d: ",&k);
for(int j=0;j<k;j++)
{
int tmp;
scanf("%d",&tmp);
if(hobby[tmp]==0) //第一个喜欢hobby[tmp]的作为这个hobby的根
hobby[tmp]=i;
//第i个人与喜欢hobby[tmp]的所有人union
Union(i,findFather(hobby[tmp]));
}
}
for(int i=1;i<=n;i++)
{
isRoot[findFather(i)]++; //这时候的根结点是每个cluster的根结点而非每个hobby的根结点。
}
int cnt=0;
for(int i=1;i<=n;i++)
if(isRoot[i]!=0) cnt++;
printf("%d\n",cnt);
sort(isRoot+1,isRoot+n+1,cmp);
printf("%d",isRoot[1]);
for(int i=2;i<=cnt;i++)
printf(" %d",isRoot[i]);
}
9.7 堆
【例】A1098 Insertion or Heap Sort (25 分)
insertion sort 插入排序
iterate v.迭代;重复
iteration n. [数] 迭代;反复;重复
iteratively adv. 迭代地;反复地
remove v.删除
shrink v.缩小
region n.地区;范围;部位
extract v.选取
ascending a.上升的,增长的,升序的
indicate v.表明;指出
ATTENTION
- 误区: 堆排序!=冒泡排序。即每次删除大顶堆的根时,是拿堆的最后一个元素代替的根,并且重新排序。而冒泡排序每次寻找最大的值的下标,将最大的值和未排序范围内的最后一个元素进行
swap
。这两者完全不同! - 再刷,堆排序还不够熟练。
#include <cstdio>
#include <algorithm>
using namespace std;
int n;
int origin[110],after[110],ans[110];
int heap[110];
bool isEqual(int after[],int ans[],int n)
{
for(int i=1;i<=n;i++)
if(after[i]!=ans[i])
return false;
return true;
}
void downAdjust(int low,int high)
{
int i=low,j=i*2;
while(j<=high)
{
if(j+1<=high&&heap[j+1]>heap[j])
j++;
if(heap[j]>heap[i])
{
swap(heap[i],heap[j]);
i=j;
j=i*2;
}
else
break;
}
}
void createHeap()
{
for(int i=n/2;i>=1;i--)
downAdjust(i,n);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&origin[i]);
heap[i]=ans[i]=origin[i];
}
createHeap();
for(int i=1;i<=n;i++)
scanf("%d",&after[i]);
for(int i=2;i<=n;i++)
{
sort(ans+1,ans+1+i);
if(isEqual(after,ans,n))
{
printf("Insertion Sort\n");
sort(ans+1,ans+1+i+1);
printf("%d",ans[1]);
for(int j=2;j<=n;j++)
{
printf(" %d",ans[j]);
}
return 0;
}
}
for(int i=n;i>1;i--)
{
swap(heap[1],heap[i]);
downAdjust(1,i-1); //无序范围!!!
if(isEqual(after,heap,n))
{
if(i>2)
{
swap(heap[1],heap[i-1]);
downAdjust(1,i-2);
}
printf("Heap Sort\n");
printf("%d",heap[1]);
for(int j=2;j<=n;j++)
{
printf(" %d",heap[j]);
}
return 0;
}
}
}
【例】A1155 Heap Paths (30 分)
specialized a.专门的
property n. 性质,性能;财产;所有权
implementation n. [计] 实现;履行;安装启用
traversal n. [计] 遍历;横越;横断物
ATTENTION
- 理智告诉我这道题不用建树,但因为搞不清中间逻辑,最后还是建了树orz
- 层序遍历层序遍历!就是按照结点
0~n-1
的顺序遍历的!所以给你的数组key[]
暗含了树结构。 - 投机取巧.jpg。因为输入的序列没有重复的元素,所以直接比较前两个元素,确定是大顶堆还是小顶堆。ps.如果不是堆并且刚好前两个元素不符合堆结构,后面会判断是不是整个序列都满足大顶堆/小顶堆,所以不会影响结果。
- 如果不建树的话,在读入
key
序列前,现将key[]
中所有元素初始化成-1
,表示这个结点为空。对于每个结点i
,它的左孩子就是2*i
,右孩子就是2*i+1
。 - 路径输出:递归遍历,每次把当前递归结点
push
进路径ans
中,在退出前再pop
出当前递归结点,实现回溯。对于当前递归节点,判断其是不是叶子(有没有孩子),这就是递归边界。不是叶子就继续递归其右孩子和左孩子。 - 判断堆:对于所有的非叶节点
[1,2/n]
,判断它的孩子和它的大小关系。注意柳神是判断孩子与父亲的大小关系的,所以不需要开大空间。但如果是判断父亲与孩子们的关系,可能最后一层只有一个结点,因此会有多个父亲的孩子可能不在key[]
中,就会出现段错误,所以要把数组开大点,确保不会下标越界。
建树:
#include <cstdio>
#include <vector>
using namespace std;
int n,level=0; //树结点数量
int type; //0 大顶堆 ; 1 小顶堆
int key[1010];
struct Node{
int data;
int left,right;
Node()
{
left=right=-1;
}
}node[1010];
vector<int> ans;
bool flag=true;
int height(int n)
{
int tmp=1,idx=1;
while(1)
{
if(n<tmp)
return idx;
tmp*=2;
idx++;
}
}
void create() //建树
{
int idx=1,cnt=1,lastCnt=0,idxKey=0;
for(int i=1;i<=level;i++)
{
idx=1;
while(idx<=cnt&&(idx+lastCnt)<=n)
{
node[idx+lastCnt].data=key[idxKey++]; //结点 1-n
if((2*(idx+lastCnt))<=n)
{
node[idx+lastCnt].left=2*(idx+lastCnt);
}
if((2*(idx+lastCnt))<n)
{
node[idx+lastCnt].right=2*(idx+lastCnt)+1;
}
idx++;
}
lastCnt=lastCnt+idx-1;
cnt*=2;
}
}
void printPath(int t)
{
ans.push_back(node[t].data);
if(node[t].left==-1&&node[t].right==-1) //到达叶子结点
{
for(int i=0;i<ans.size();i++)
{
if(i==0) printf("%d",ans[i]);
else printf(" %d",ans[i]);
}
printf("\n");
ans.pop_back();
return ;
}
if(node[t].right!=-1)
printPath(node[t].right);
if(node[t].left!=-1)
printPath(node[t].left);
ans.pop_back();
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&key[i]);
}
if(key[0]>key[1]) type=0; //大顶堆
else type=1; //小顶堆
level=height(n);
create();
printPath(1);
for(int i=1;i<=n/2;i++)
{
int max=0,min=0;
if(node[i].left!=-1)
{
max=node[node[i].left].data;
min=node[node[i].left].data;
}
if(node[i].right!=-1)
{
if(max<node[node[i].right].data) max=node[node[i].right].data;
if(min>node[node[i].right].data) min=node[node[i].right].data;
}
if(type==0)
{
if(max!=0&&node[i].data<max)
{
flag=false;
printf("Not Heap\n");
break;
}
}
if(type==1)
{
if(min!=0&&node[i].data>min)
{
flag=false;
printf("Not Heap\n");
break;
}
}
}
if(type==0&&flag)
{
printf("Max Heap\n");
}
if(type==1&&flag)
{
printf("Min Heap\n");
}
return 0;
}
不建树:
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
int n,level=0; //树结点数量
int type; //0 大顶堆 ; 1 小顶堆
int key[5000];
vector<int> ans;
bool flag=true;
void printPath(int t)
{
if(t<=n)
ans.push_back(key[t]);
if(key[2*t]==-1&&key[2*t+1]==-1) //到达叶子结点
{
for(int i=0;i<ans.size();i++)
{
if(i==0) printf("%d",ans[i]);
else printf(" %d",ans[i]);
}
printf("\n");
ans.pop_back();
return ;
}
if(key[2*t+1]!=-1)
printPath(2*t+1);
if(key[2*t]!=-1)
printPath(2*t);
ans.pop_back();
}
int main()
{
fill(key,key+5000,-1); //初始化key,即-1表示NULL。
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&key[i]);
}
if(key[1]>key[2]) type=0; //大顶堆
else type=1; //小顶堆
printPath(1);
for(int i=1;i<=n/2;i++)
{
int max=0,min=0;
if(2*i<=n&&key[2*i]!=-1)
{
max=key[2*i];
min=key[2*i];
}
if(2*i+1<=n&&key[2*i+1]!=-1)
{
if(max<key[2*i+1]) max=key[2*i+1];
if(min>key[2*i+1]) min=key[2*i+1];
}
if(type==0)
{
if(max!=0&&key[i]<max)
{
flag=false;
printf("Not Heap\n");
break;
}
}
if(type==1)
{
if(min!=0&&key[i]>min)
{
flag=false;
printf("Not Heap\n");
break;
}
}
}
if(type==0&&flag)
{
printf("Max Heap\n");
}
if(type==1&&flag)
{
printf("Min Heap\n");
}
return 0;
}
【例】A1147 Heaps (30 分)
水题
#include <cstdio>
#include <vector> //初始化大小开辟空间吗?
using namespace std;
int n,m;
vector<int> post;
bool isMax(vector<int> heap,int m)
{
for(int i=1;i<=m/2;i++)
{
int j=2*i;
if(j<m&&heap[j+1]>heap[j]) j++;
if(heap[i]<heap[j]) return false;
}
return true;
}
bool isMin(vector<int> heap,int m)
{
for(int i=1;i<=m/2;i++)
{
int j=2*i;
if(j<m&&heap[j+1]<heap[j]) j++;
if(heap[i]>heap[j]) return false;
}
return true;
}
void postOrder(int r)
{
if(r>m) return;
postOrder(2*r);
postOrder(2*r+1);
post.push_back(r);
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++)
{
vector<int> heap(m+1);
for(int j=1;j<=m;j++)
scanf("%d",&heap[j]);
if(isMax(heap,m)==true)
printf("Max Heap\n");
else if(isMin(heap,m)==true)
printf("Min Heap\n");
else
printf("Not Heap\n");
post.clear();
postOrder(1);
for(int j=0;j<m;j++)
{
if(j==0) printf("%d",heap[post[j]]);
else printf(" %d",heap[post[j]]);
}
printf("\n");
}
}