pat树问题总结2 dfs,bfs

目录

  • 根据前中后三种遍历构建树
  • dfs,bfs
  • 建树

注:dfs写出路径的一般写法,先写出边界条件,在此条件下输出保存路径的数组,然后通过下面所示的方法,将路径的index放入保存路径的数组,并递归。(结合A1053和A1155)

(A1053)给出树的结构和权值,找出从根节点到叶子结点路径上权值相加之和等于给定数字的路径,并且从大到小输出路径。
分析:模板题。在递归过程中保存路径有两种方法。
pat树问题总结2 dfs,bfs_第1张图片
其中,树可以写成静态的也可以写成动态的。

#include
#include
#include
using namespace std;
const int maxn=110;
struct Node{
	int weight;
	vector<int> child;
}node[maxn];

bool cmp(int a,int b){
	return node[a].weight>node[b].weight;
}
int n,m,k;
int path[maxn];

void DFS(int index,int numnode,int sum){
	if(sum>k) return; //边界 
	if(sum==k){ //边界 
		if(node[index].child.size()!=0) return;
		for(int i=0;i<numnode;i++){
			printf("%d",node[path[i]].weight);
			if(i<numnode-1){
				printf(" ");
			}
			else printf("\n");
		}
		return;
	}
	for(int i=0;i<node[index].child.size();i++){
		int child=node[index].child[i];
		path[numnode]=child;
		DFS(child,numnode+1,sum+node[child].weight);
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=0;i<n;i++){
		scanf("%d",&node[i].weight);
	}
	int id,k,child;
	for(int i=0;i<m;i++){
		scanf("%d%d",&id,&k);
		for(int j=0;j<k;j++){
			scanf("%d",&child);
			node[id].child.push_back(child);
		}
		sort(node[id].child.begin(),node[id].child.end(),cmp);
	}
	path[0]=0;
	DFS(0,1,node[0].weight);
	return 0;
}

(A1155)给出一棵完全二叉树,打印从根节点到所有叶节点的路径,打印顺序先右后左,即先序的镜像。然后判断该树是大顶堆,小顶堆还是不是堆。
分析:利用dfs打印出路径,其中dfs打印路径可以使用第二种方法。判断是否为堆:从第二个结点开始遍历,如果比父节点小,就不是小顶堆,比父节点大,就不是大顶堆。

#include 
using namespace std;
int n;
int an[2005];
bool isbig = true, islittle = true;
vector<int> v;
void dfs(int x){
    if(x*2 > n && x*2+1 > n){
        for(int i = 0; i < v.size(); i++){
            printf("%d%c", v[i], i == v.size()-1?'\n':' ');
        }
    }else{
        if(x*2+1 <= n){
            v.push_back(an[x*2+1]);
            dfs(x*2+1);
            v.pop_back();
        }
        if(x*2 <= n){
            v.push_back(an[x*2]);
            dfs(x*2);
            v.pop_back();
        }
    }
}

int main(){
    cin >> n;
    for(int i = 1 ; i <= n ; ++i){
        cin >> an[i];
        if(i >= 2){
            if(an[i/2] < an[i]) isbig = false;
            if(an[i/2] > an[i]) islittle = false;
        }
    }
    v.push_back(an[1]);
    dfs(1);
    if(isbig){
        printf("Max Heap");
    }else if(islittle){
        printf("Min Heap");
    }else{
        printf("Not Heap");
    }
    return 0;
}
}

(A1147)给⼀个树的层序遍历,判断它是不是堆,是⼤顶堆还是⼩顶堆。输出这个树的后序遍历。

#include 
#include 
using namespace std;
int m, n;
vector<int> v;
void postOrder(int index) { //后续遍历
    if (index >= n) return;
    postOrder(index * 2 + 1);
    postOrder(index * 2 + 2);
    printf("%d%s", v[index], index == 0 ? "\n" : " ");
}
int main() {
    scanf("%d%d", &m, &n);
    v.resize(n);
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) scanf("%d", &v[j]);
        int flag = v[0] > v[1] ? 1 : -1;
        for (int j = 0; j <= (n-1) / 2; j++) { //遍历所有有孩子的结点
            int left = j * 2 + 1, right = j * 2 + 2;
            if (flag == 1 && (v[j] < v[left] || (right < n && v[j] < v[right]))) flag = 0;
            if (flag == -1 && (v[j] > v[left] || (right < n && v[j] > v[right]))) flag = 0;
        }
        if (flag == 0) printf("Not Heap\n");
        else printf("%s Heap\n", flag == 1 ? "Max" : "Min");
        postOrder(0);
    }
    return 0;
}

(A1004)给出一棵树,问每一层各有多少个叶节点。
分析:本题可以用dfs和bfs做。dfs用一个hashtable保存每层的结点个数即可。而树用什么保存,依题目的输入来定,本题用一个vector G[N]保存序号即可。
注意:vector数组vector G[n]的意思是限定第一维的元素有n个,可以有第二维,且第二维的元素不限个数。
dfs

#include
#include
#include
using namespace std;
const int maxn=110;

vector<int> G[maxn];
int leaf[maxn]={0};
int max_h=1;

void dfs(int index,int h){
	max_h=max(max_h,h);
	if(G[index].size()==0){
		leaf[h]++;
		return;
	}
	for(int i=0;i<G[index].size();i++){
		dfs(G[index][i],h+1);
	}
}
int main(){
	int n,m,parent,child,k;
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++){
		scanf("%d%d",&parent,&k);
		for(int j=0;j<k;j++){
			scanf("%d",&child);
			G[parent].push_back(child);
		}
	}
	dfs(1,1);
	printf("%d",leaf[1]);
	for(int i=2;i<=max_h;i++){
		printf(" %d",leaf[i]);
	}
	return 0;
}

bfs

#include
#include
#include
using namespace std;
const int maxn=110;
vector<int> G[maxn];
int h[maxn]={0}; //各结点的层号 
int left[maxn]={0};
int max_h=0;

void BFS(){
	queue<int> q;
	q.push(1); 
	while(!q.empty()){
		int id=q.front();
		q.pop();
		max_h=max(max_h,h[id]);
		if(G[id].size()==0){
			left[h[id]]++;
		}
		for(int i=0;i<G[id].size();i++){
			h[G[id][i]]=h[id]+1; //子节点
			q.push(G[id][i]); 
		}
	}
} 
int main(){
	int n,m,parent,child,k;
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++){
		scanf("%d%d",&parent,&k);
		for(int j=0;j<k;j++){
			scanf("%d",&child);
			G[parent].push_back(child); //child是parent的子节点(加边) 
		}
	}
	h[1]=1;
	BFS();
	for(int i=1;i<=max_h;i++){
		if(i==1) printf("%d",left[i]);
		else printf(" %d",left[i]);
	}
	return 0;
} 

(A1106)提供一棵树,在树根处货物的价格为p,从根节点开始往下每一层,该层货物的价格将会在父节点的价格上增加r%。求叶子结点处能获得的最低价格以及能提供最低价格的叶子结点个数。
分析:用min_depth保存最小深度,用min_num保存最小深度结点个数,然后dfs即可。
注意:输入时,double对应的是%lf,输出时,保留四位小数的写法是%.4f。平方函数pow的用法是pow(基数,几次方)。

#include 
#include 
#include 
using namespace std;
vector<int> v[100005];
int mindepth = 99999999, minnum = 1;
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);
}
int main() {
    int n, k, c;
    double p, r;
    scanf("%d %lf %lf", &n, &p, &r);
    for(int i = 0; i < n; i++) {
        scanf("%d", &k);
        for(int j = 0; j < k; j++) {
            scanf("%d", &c);
            v[i].push_back(c);
        }
    }
    dfs(0, 0);
    printf("%.4f %d", p * pow(1 + r/100, mindepth), minnum);
    return 0;
}

(A1102)二叉树有N个结点(结点编号为0~N-1),给出每个结点的左右孩子的编号,把该二叉树反转(即把每个结点的左右子树都变换),输出反转后二叉树的层序遍历和中序遍历序列。
分析:dfs得到镜像中序遍历,在dfs中添加id及level,以便于后面输出层序遍历使用。层序遍历用id号和层号足以输出,根节点通过遍历孩子结点,在孩子结点中没出现过的结点就是根节点。

#include
#include
#include 
using namespace std;
struct node{
	int id,l,r,index,level;
}a[100];

vector<node> v1;
void dfs(int root,int index,int level){ //镜面前序 
	if(a[root].r!=-1){
		dfs(a[root].r,index*2+2,level+1);
	}
	v1.push_back({root,0,0,index,level});
	if(a[root].l!=-1){
		dfs(a[root].l,index*2+1,level+1);
	}
}
bool cmp(node a,node b){ //按照层数和编号足以输出层序 
	if(a.level!=b.level) return a.level<b.level;
	return a.index>b.index;
}
int main(){
	int n,hash[100]={0},root=0;
	cin>>n;
	for(int i=0;i<n;i++){
		a[i].id=i;
		string l,r;
		cin>>l>>r;
		if(l!="-"){
			a[i].l=stoi(l);
			hash[stoi(l)]=1;
		}
		else{
			a[i].l=-1;
		}
		if(r!="-"){
			a[i].r=stoi(r);
			hash[stoi(r)]=1;
		}
		else{
			a[i].r=-1;
		}
	}
	while(hash[root]==1) root++; //根节点就是层节点个数为1的 
	dfs(root,0,0);
	vector<node> v2(v1); //用v1来创建v2vector,相当于将v1所有复制到v2 
	sort(v2.begin(),v2.end(),cmp);
	for(int i=0;i<v2.size();i++){ //层序 
		if(i!=0) cout<<" ";
		cout<<v2[i].id;
	}
	cout<<endl;
	for(int i=0;i<v1.size();i++){ //中序 
		if(i!=0) cout<<" ";
		cout<<v1[i].id;
	}
	return 0;
}

(A1110)给出n个结点及其孩子,判断这颗树是不是完全二叉树
分析:基本上和上题一样,关键在于找到根节点

#include 
using namespace std;
struct node{
    int l, r;
}a[100];
int maxn = -1, ans;
void dfs(int root, int index) {
    if(index > maxn) {
        maxn = index;
        ans = root;
    }
    if(a[root].l != -1) dfs(a[root].l, index * 2);
    if(a[root].r != -1) dfs(a[root].r, index * 2 + 1);
}
int main() {
    int n, root = 0, have[100] = {0};
    cin >> n;
    for (int i = 0; i < n; i++) {
        string l, r;
        cin >> l >> r;
        if (l == "-") {
            a[i].l = -1;
        } else {
            a[i].l = stoi(l);
            have[stoi(l)] = 1;
        }
        if (r == "-") {
            a[i].r = -1;
        } else {
            a[i].r = stoi(r);
            have[stoi(r)] = 1;
        }
    }
    while (have[root] != 0) root++;
    dfs(root, 1);
    if (maxn == n)
        cout << "YES " << ans;
    else
        cout << "NO " << root;
    return 0;
}

(A1099)给出一棵二叉搜索树(给出每个结点的左右孩子),且已知根节点为0,并且给出应该插入这个二叉搜索树的数值,求这颗二叉搜索树的层序遍历。
分析:对一棵二叉搜索树来说,中序遍历序列是递增的,所以只需要把给定的序列递增排序,然后对给定的二叉搜索树进行中序遍历,将排序后序列的整数按中序遍历的顺序填入二叉树,然后再调用bfs即可。

#include
#include
#include
using namespace std;
const int maxn=110;
struct Node{
	int data;
	int lchild,rchild;
}node[maxn];
int n,in[maxn],num=0;
void inorder(int root){
	if(root==-1) return;
	inorder(node[root].lchild);
	node[root].data=in[num++];
	inorder(node[root].rchild);
}
void BFS(int root){
	queue<int> q;
	q.push(root);
	num=0;
	while(!q.empty()){
		int now=q.front();
		q.pop();
		printf("%d",node[now].data);
		num++;
		if(num<n){
			printf(" ");
		}
		if(node[now].lchild!=-1){
			q.push(node[now].lchild);
		}
		if(node[now].rchild!=-1){
			q.push(node[now].rchild);
		}
	}
}
int main(){
	int lchild,rchild;
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d%d\n",&lchild,&rchild);
		node[i].lchild=lchild;
		node[i].rchild=rchild;
	}
	for(int i=0;i<n;i++){
		scanf("%d",&in[i]);
	}
	sort(in,in+n);
	inorder(0);
	BFS(0);
	return 0;
}

(A1130)给一个二叉搜索树,输出中缀表达式,且加上括号表示运算的优先级。

#include
#include
#include
#include
using namespace std;
struct node{
	string data;
	int l,r;
}a[25];
string dfs(int root){
	if(a[root].l==-1&&a[root].r==-1){
		return a[root].data;
	}
	if(a[root].l==-1&&a[root].r!=-1){
		return "(" + a[root].data + dfs(a[root].r) + ")";
	}
	if(a[root].l!=-1&&a[root].r!=-1){
		return "(" + dfs(a[root].l) + a[root].data + dfs(a[root].r) + ")";
	}
}
int main(){
	int hash[100]={0},n,root=1;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i].data>>a[i].l>>a[i].r;
		if(a[i].l!=-1) hash[a[i].l]=1;
		if(a[i].r!=-1) hash[a[i].r]=1;
	}
	while(hash[root]==1) root++;
	string ans=dfs(root);
	if(ans[0]=='('){
		ans=ans.substr(1,ans.size()-2);
	}
	cout<<ans;
	return 0;
}

你可能感兴趣的:(树,算法笔记和模板)