PAT之树:一般树、二叉树、完全二叉树、二叉搜索树、二叉平衡树、并查集

这篇博客详细介绍了PAT考试中关于树和二叉树的相关知识点,包括一般树的遍历、路径计算、公共祖先查询,以及二叉树的前中后序遍历、完全二叉树的判断、二叉搜索树的构建和操作,二叉平衡树AVL的构造,堆的判断和路径问题,并查集的运用和实现。内容涵盖模板代码和解题技巧,适合数据结构学习者参考。
摘要由CSDN通过智能技术生成

0 模板

二叉树-模板

输入:

9
7 8
- -
- -
- -
0 1
2 3
4 5
- -
- -

代码:

#include<cstdio>
#include<string>
#include<iostream>
#include<queue>

using namespace std;

typedef struct Node{
	int data;
	int l,r;
	//
	int order;	//BFS的次序:从1开始
	int level;
	Node(){	l=r=-1;	}
}Node;

vector<Node> tree; //存储从0开始
int ROOT;

// 一般dfs:先序
void preOrder(int root, int level){
	if(root==-1) return;
	//
	printf("%d", root);
	tree[root].level = level;
	//
	preOrder(tree[root].l, level+1);
	preOrder(tree[root].r, level+1);
}

void inOrder(int root){
	if(root==-1) return;
	inOrder(tree[root].l);
	//
	printf("%d", root);
	//
	inOrder(tree[root].r);
}

void postOrder(int root){
	if(root==-1) return;
	postOrder(tree[root].l);
	postOrder(tree[root].r);
	//
	printf("%d", root);
	//
}

int depth=0;
//层次遍历:一层层结点
void bfs(int root){
	queue<int> Q;
	Q.push(root);
	int level=1;
	while(!Q.empty()){
		if(level>depth){
				depth = level;
			}
		int size = Q.size();
		// Q中为同一层
		for(int i=0; i<size; i++){
			int f = Q.front();
			Q.pop();
			if(tree[f].l!=-1){
				Q.push(tree[f].l);
			}
			if(tree[f].r!=-1){
				Q.push(tree[f].r);
			}
		}
		//
		level++;
	}
}


//层次遍历:一个个结点
void levelOrder(int root){
	queue<int> Q;
	Q.push(root);
	//
	tree[ROOT].level = 1;
	int order = 0;
	//
	while(!Q.empty()){
		int f = Q.front();			
		//
		order++;
		tree[f].order  = order;
		printf("%d %d %d\n",tree[f].order,tree[f].level,f);
		//
		int left = tree[f].l;
		int right = tree[f].r;
		if(left!=-1){
			tree[left].level = tree[f].level+1; 
			Q.push(left);
		}
		if(right!=-1){
			tree[right].level = tree[f].level+1; 
			Q.push(right);
		}	
		//
		Q.pop();			//!!!		
	}
}

int main(){
    //freopen("in.txt","r", stdin);

	int n;
	scanf("%d", &n);
	tree.resize(n);
	int root=0;
	bool isChild[N]={false};	//寻找root
	for(int i=0; i<n; i++){
		string s_l,s_r;
		cin>>s_l>>s_r;
		if(s_l!="-"){
			tree[i].l = stoi(s_l);
			isChild[tree[i].l] = true;
		}
		if(s_r!="-"){
			tree[i].r = stoi(s_r);
			isChild[tree[i].r] = true;
		}
	}
	// 寻找root
	while(isChild[root]){
		root++;
	}
	ROOT = root;
	// 4种遍历
	preOrder(root,1);
	printf("\n");
	inOrder(root);
	printf("\n");
	postOrder(root);
	printf("\n");
	levelOrder(root);

    //fclose(stdin);
    return 0;
}

树-模板

#include<cstdio>
#include<vector>
#include<queue>

using namespace std;


const int N=110;

typedef struct Node{
	int data;
	vector<int> child;
}Node;

Node tree[N];
int ROOT;

int depth=0;

void dfs(int root, int level){		//类似 先序
	if(level>depth){
			depth = level;
	}
	for(int i=0; i<tree[root].child.size(); i++){
		dfs(tree[root].child[i], level+1);
	}
} 


void bfs(int root){
	queue<int> Q;
	Q.push(root);
	int level=1;
	while(!Q.empty()){
		if(level>depth){
			depth = level;
		}
		// Q中为同一层node
		int size = Q.size();
		for(int i=0; i<size; i++){
			int f = Q.front();
			Q.pop();
			// 将f的child入队
			for(int j=0; j<tree[f].child.size(); j++){
				Q.push(tree[f].child[j]);
			}
		}
		level++;		//下一层的level
	}
}


int main(){
    //freopen("in.txt","r", stdin);

	int n,m;
	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 t;
			scanf("%d", &t);
			tree[id].child.push_back(t);
		}
	}	
	ROOT = 1;
	dfs(ROOT, 1);
	printf("%d\n",depth);
	bfs(ROOT);
	printf("%d\n",depth);


    //fclose(stdin);
    return 0;
}


1 一般树

1.1 遍历

1094(25:每一层的结点个数 dfs/bfs)

(1)题目
求哪一层的结点个数最多

(2) 代码

方法1:dfs

#include<cstdio>
#include<vector>

using namespace std;


const int N=110;

typedef struct Node{
	int data;
	vector<int> child;
}Node;

Node tree[N];
int ROOT;


int level_node_num[N];		// 1 到 depth
int depth=0;		//树的深度

void dfs(int root, int level){
	level_node_num[level]++;
	if(level>depth){
		depth = level;
	}
	for(int i=0; i<tree[root].child.size(); i++){
		dfs(tree[root].child[i], level+1);
	}
}


int main(){
    //freopen("in.txt","r", stdin);

	int n,m;
	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 t;
			scanf("%d", &t);
			tree[id].child.push_back(t);
		}
	}	
	ROOT = 1;
	dfs(ROOT, 1);
	//
	int max_node = 0; 
	int level_of_node=0;
	for(int i=1; i<=depth; i++){
		if(level_node_num[i] > max_node){
			max_node = level_node_num[i];
			level_of_node = i;
		}
	}
	printf("%d %d\n",max_node,  level_of_node);


    //fclose(stdin);
    return 0;
}


方法2:bfs

#include<cstdio>
#include<vector>
#include<queue>

using namespace std;


const int N=110;

typedef struct Node{
	int data;
	vector<int> child;
}Node;

Node tree[N];
int ROOT;

int level_node_num[N];		// 1 到 depth
int depth=0;		//树的深度

void bfs(int root){
	queue<int> Q;
	Q.push(root);
	int level=1;
	while(!Q.empty()){
		if(level>depth){
			depth = level;
		}
		// Q中为同一层node
		int size = Q.size();
		level_node_num[level] = size;
		for(int i=0; i<size; i++){
			int f = Q.front();
			Q.pop();
			// 将f的child入队
			for(int j=0; j<tree[f].child.size(); j++){
				Q.push(tree[f].child[j]);
			}
		}
		level++;		//下一层的level
	}
}


int main(){
    //freopen("in.txt","r", stdin);

	int n,m;
	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 t;
			scanf("%d", &t);
			tree[id].child.push_back(t);
		}
	}	
	ROOT = 1;
	bfs(ROOT);
	//
	int max_node = 0; 
	int level_of_node=0;
	for(int i=1; i<=depth; i++){
		if(level_node_num[i] > max_node){
			max_node = level_node_num[i];
			level_of_node = i;
		}
	}
	printf("%d %d\n",max_node,  level_of_node);

    //fclose(stdin);
    return 0;
}

(3)小结

  • bfs:区分是一个个结点处理,还是一层层结点处理

1004(30:每一层叶子结点个数 dfs/bfs)

(1)题目
求每一层叶子结点个数

(2)代码

方法1:dfs

#include<cstdio>
#include<vector>

using namespace std;


const int N=110;

typedef struct Node{
	int data;
	vector<int> child;
}Node;

Node tree[N];
int ROOT;

int level_leaf_num[N];		//每层叶子节点个数, 1 到 depth
int depth=0;		//树的深度

void dfs(int root, int level){
	if(level>depth){
		depth = level;
	}
	if(tree[root].child.size()==0){
		level_leaf_num[level]++;
	}
	//
	for(int i=0; i<tree[root].child.size(); i++){
		dfs(tree[root].child[i], level+1);
	}
}

int main(){
    //freopen("in.txt","r", stdin);

	int n,m;
	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 t;
			scanf("%d", &t);
			tree[id].child.push_back(t);
		}
	}	
	ROOT = 1;
	dfs(ROOT, 1);
	//
	for(int i=1; i<=depth; i++){
		if(i!=1){
			printf(" ");
		}
		printf("%d", level_leaf_num[i]);
	}

    //fclose(stdin);
    return 0;
}

方法2:bfs

#include<cstdio>
#include<vector>
#include<queue>

using namespace std;


const int N=110;

typedef struct Node{
	int data;
	vector<int> child;
}Node;

Node tree[N];
int ROOT;

int level_leaf_num[N];		//每层叶子节点个数, 1 到 depth
int depth=0;		//树的深度

void bfs(int root){
	queue<int> Q;
	Q.push(root);
	int level=1;
	while(!Q.empty()){
		if(level>depth){
			depth = level;
		}
		// Q中为同一层node
		int size = Q.size();
		int cnt=0;		//该层叶子节点个数
		for(int i=0; i<size; i++){
			int f = Q.front();
			Q.pop();
			if(tree[f].child.size()==0){
				cnt++;
			}
			// 将f的child入队
			for(int j=0; j<tree[f].child.size(); j++){
				Q.push(tree[f].child[j]);
			}
		}
		level_leaf_num[level] = cnt;
		level++;		//下一层的level
	}
}


int main(){
    //freopen("in.txt","r", stdin);

	int n,m;
	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 t;
			scanf("%d", &t);
			tree[id].child.push_back(t);
		}
	}	
	ROOT = 1;
	bfs(ROOT);
	//
	for(int i=1; i<=depth; i++){
		if(i!=1){
			printf(" ");
		}
		printf("%d", level_leaf_num[i]);
	}

   // fclose(stdin);
    return 0;
}

1.2 路径(dfs)

1053(30:路径的权重和 + path)

(1)
题目:
路径上根到叶的权重和(结点权重)+ path

(2)

#include<cstdio>
#include<vector>
#include<algorithm>

using namespace std;

const int N=100+10;

typedef struct Node{
	int data;
	vector<int> child;
}Node;

Node tree[N];
int ROOT;

int path[N];		
// path[i] = j:  在一个路径上,第i层的结点是tree[j]

int S;


void dfs(int root, int sum, int level){		
	if(tree[root].child.size()==0){	//在叶子节点进行判断
		if(S==sum){
			//输出path
			for(int i=1; i<=level; i++){
				if(i!=1) printf(" ");
				printf("%d", tree[path[i]].data);
			}
			printf("\n");
		}
		return;
	}
	
	for(int i=0; i<tree[root].child.size(); i++){		
		int child = tree[root].child[i];
		path[level+1] = child;
		//sum += tree[child].data;		//错误
		//dfs(child, sum);
		dfs(child, sum+tree[child].data, level+1);
	}
}


bool cmp(int index1, int index2){
	return tree[index1].data > tree[index2].data;
}

int main(){
	//freopen("in.txt", "r", stdin);

	int n,m;
	scanf("%d%d%d", &n, &m, &S);
	for(int i=0; i<n; i++){
		scanf("%d", &tree[i].data);
	}
	for(int i=0; i<m; i++){
		int id,k;
		scanf("%d%d", &id, &k);
		for(int j=0; j<k; j++){
			int t;
			scanf("%d", &t);
			tree[id].child.push_back(t);
		}
		// 对tree[id].child进行排序:根据data由大到小
		//(dfs时搜索到的结果已经自动变为由大到小,直接输出即可)
		sort(tree[id].child.begin(), tree[id].child.end(), cmp);
	}
	ROOT = 0;
	//
	int	sum = tree[ROOT].data;
	path[1] = ROOT;	//第1层的结点是ROOT
	dfs(ROOT, sum, 1);

	//fclose(stdin);
	return 0;
}

(3)小结

  • 技巧:若输出的多条路径要根据data从大到小排序,则提前对每个点的child根据data从大到小排序,这样dfs输出的路径直接就是排好序的。
  • dfs的参数问题:
    有两种写法,看需要哪种
void dfs(int sum){
	...
	sum += 100;
	dfs(sum);
	...
}void dfs(int sum){
	...
	dfs(sum+100);
	...
}

如:计算结点层次

void dfs_level(int root, int level){
	if(tree[root].child.size()==0){ 
		return;
	}
	for(int i=0; i<tree[root].child.size(); i++){		
		int child = tree[root].child[i];
		......
	}
}

.省略号处,假设有以下三种写法,比较结果(假设执行省略号前,level=3)。
B等价于C,比较A和C的写法

//A.
level++;
dfs(child, level);
//B.
int temp = level+1;
dfs(child, temp);
//C.
dfs(child, level+1);

分析:
在 省略号处(A或C) 执行完后,A中的level变为4,而B中的level不变仍为3。
因此,正确写法:B或C

  • 记录路径问题:path
int path[N];		
// path[i] = j:  在一个路径上,第i层的结点是tree[j]
void dfs(int level){		//最终的level值,路径上共有多少个结点
	...
}  

1079(25:路径的权重和)

(1)题目
根到所有叶的权重之和

(2)代码

#include<cstdio>
#include<vector>
#include<cmath>

using namespace std;

typedef struct Node{
	int data;
	vector<int> child;
}Node;

vector<Node> tree;		//N=10^5,很大
int ROOT;

double p,r;
double total=0;


void dfs(int root, int level){
	if(tree[root].child.size()==0){	
		total += (p * tree[root].data * pow(r+1, level-1));
		return;
	}
	for(int i=0; i<tree[root].child.size(); i++){
		dfs(tree[root].child[i], level+1);
	}
}




int main(){
	freopen("in.txt", "r", stdin);

	int n;
	scanf("%d%lf%lf", &n, &p, &r);
	r = r/100;		// !!!
	tree.resize(n);		//!!!
	for(int i=0; i<n; i++){
		int k;
		scanf("%d", &k);
		if(k==0){
			scanf("%d", &tree[i].data);
		}else{
			for(int j=0; j<k; j++){
				int t;
				scanf("%d", &t);
				tree[i].child.push_back(t);
			}
		}
	}
	ROOT = 0;
	//
	dfs(ROOT, 1);
	printf("%.1f", total);

	fclose(stdin);
	return 0;
}

(3)小结

  • 当树的结点 N= 1 0 5 10^5 105时,用vector<Node> tree,而不是Node tree[N]
vector<Node> tree;

// 输入n
tree.resize(n);

1090(25:路径的权重最大值)

(1)题目

根到所有叶子结点中权重的最大值与个数

(2)代码

#include<cstdio>
#include<vector>
#include<cmath>

using namespace std;

typedef struct Node{
	int data;
	vector<int> child;
}Node;

vector<Node> tree;		//N=10^5,很大
int ROOT;

double p,r;
vector<double> price;	//每个叶子结点的最终price


void dfs(int root, int level){
	if(tree[root].child.size()==0){	
		double sum = p * pow(r+1, level-1);
		price.push_back(sum);
		return;
	}
	for(int i=0; i<tree[root].child.size(); i++){
		dfs(tree[root].child[i], level+1);
	}
}

int main(){
	//freopen("in.txt", "r", stdin);

	int n;
	scanf("%d%lf%lf", &n, &p, &r);
	r = r/100;		// !!!
	tree.resize(n);		//!!!
	for(int i=0; i<n; i++){
		int t;
		scanf("%d", &t);
		if(t==-1){
			ROOT = i;
		}else{
			tree[t].child.push_back(i);
		}
	}
	//
	dfs(ROOT, 1);
	double high=-1;
	int num=0;
	for(int i=0; i<price.size(); i++){
		if(price[i]>high){
			high = price[i];
			num = 1;
		}else if(price[i]==high){
			num++;
		}
	}
	printf("%.2f %d", high, num);

	//fclose(stdin);
	return 0;
}

(3)小结

  • Then the next line contains N numbers, each number S​i is the index of the supplier for the i-th member. S​root​ for the root supplier is defined to be −1.
    S:1 5 4 4 -1 4 5 3 6
    i:0 1 2 3 4 5 6 7 8
    tree[4].child.push_back(2);
    root = 4;

1106(25:路径的权重最小值)

(1)题目
根到所有叶子结点中权重的最小值与个数

(2)代码

#include<cstdio>
#include<vector>
#include<cmath>

using namespace std;

typedef struct Node{
	int data;
	vector<int> child;
}Node;

vector<Node> tree;		//N=10^5,很大
int ROOT;

double p,r;
vector<double> price;	//每个叶子结点的最终price


void dfs(int root, int level){
	if(tree[root].child.size()==0){	
		double sum = p * pow(r+1, level-1);
		price.push_back(sum);
		return;
	}
	for(int i=0; i<tree[root].child.size(); i++){
		dfs(tree[root].child[i], level+1);
	}
}

int main(){
	//freopen("in.txt", "r", stdin);

	int n;
	scanf("%d%lf%lf", &n, &p, &r);
	r = r/100;		// !!!
	tree.resize(n);		//!!!
	for(int i=0; i<n; i++){
		int k;
		scanf("%d", &k);
		if(k!=0){
			for(int j=0; j<k; j++){
				int t;
				scanf("%d", &t);
				tree[i].child.push_back(t);
			}
		}
	}
	ROOT = 0;
	//
	dfs(ROOT, 1);
	double low=price[0];
	int num=1;
	for(int i=1; i<price.size(); i++){
		if(price[i]<low){
			low = price[i];
			num = 1;
		}else if(price[i]==low){
			num++;
		}
	}
	printf("%.4f %d", low, num);

	//fclose(stdin);
	return 0;
}

1.3 公共祖先

  • 和父辈有关:设置成员 p = -1 (父指针)

2 二叉树

2.1 前中后序

模板

(1)小结

  • 前中后序问题:有两种情况
    若:已知前中求后、已知前后求中(不唯一),已知中后求前:
    则,不建树,边dfs边求
    若:和树的其他相关:
    则,建树,先dfs再处理

代码:
题型1:不建树,边dfs边求(无返回值void)

vector<int> pre, in, post;

/*
n=8
中:12 11 20 17 1 15 8 5
后:12 20 17 11 15 8 5 1
输出:
前:1 11 12 17 5 8 15
*/
// 已知中序和后序,求前序
void dfs(int inL, int inR, int postL, int postR){	//初始:0 到 n-1
	if(inL > inR) return;
	int root = postR;			//此子树的根节点在post中的postR位置
	//
	pre.push_back(post[root]);
	//
	int i=0;					//此子树的根节点在in中的i位置
	while(in[i]!=post[root]){	//结束:in[i] == post[root]
		i++;
	} 		
	int llen = i - inL;			// 左子树长度:(i-1) - inL +1 
	int rlen = inR - i;			// inR - (i+1) +1
	if(llen!=0){
		dfs(inL, i-1, postL, llen+postL-1);		//根节点的左子树
	}
	if(rlen!=0){
		dfs(i+1, inR, postR-rlen, postR-1);
	}
}

/*
n=7
前:1 2 3 4 5 6 7
中:2 3 1 5 4 7 6

输出:
3 2 5 7 6 4 1
*/
// 已知前序和中序,求后序
void dfs(int preL, int preR, int inL, int inR){	//初始:0 到 n-1
	if(inL > inR) return;
	int root = preL;		//此子树的根节点在pre中的preL位置
	int i=0;				//此子树的根节点在in中的i位置
	while(in[i]!=pre[root]){
		i++;
	}						//结束:in[i] == pre[root]
	int llen = i - inL;		// 左子树长度:(i-1) - inL +1 
	int rlen = inR - i;		// inR - (i+1) +1
	if(llen!=0){
		dfs(preL+1, preL+llen,inL, i-1);		//根节点的左子树
	}
	if(rlen!=0){
		dfs(preR-rlen+1, preR, i+1, inR);
	}
	post.push_back(pre[root]);
}

题型2:建树(有返回值)

const int N=50+10;
typedef struct Node{
	int data;
	int l,r;
	Node(){	l = r = -1;}
}Node;
Node tree[N];
int ROOT;

vector<int> pre, in, post;

// 根据中序和后序建树:有返回值
int dfs_build(int inL, int inR, int postL, int postR){	//初始:0 到 n-1
	if(inL > inR) return;
	int root = postR;			//此子树的根节点在post中的postR位置
	//	
	tree[root].data = post[postR];
	//
	int i=0;					//此子树的根节点在in中的i位置
	while(in[i]!=post[root]){
		i++;
	} //结束:in[i] == post[root]
	int llen = i - inL;			// (i-1) - inL +1 
	int rlen = inR - i;			// inR - (i+1) +1
	if(llen!=0){
		tree[root].l = dfs_build(inL, i-1, postL, llen+postL-1);		//根节点的左子树
	}
	if(rlen!=0){
		tree[root].r = dfs_build(i+1, inR, postR-rlen, postR-1);
	}
	return root;
}

//根据前序和中序:建树
int dfs_build(int preL, int preR, int inL, int inR){	//初始:0 到 n-1
	int root = preL;		//此子树的根节点在pre中的preL位置
	tree[root].data = pre[root];
	int i=0;				//此子树的根节点在in中的i位置
	while(in[i]!=pre[root]){
		i++;
	} 						//结束:in[i] == pre[root]
	int llen = i - inL;		// (i-1) - inL +1 
	int rlen = inR - i;		// inR - (i+1) +1
	if(llen!=0){
		tree[root].l = dfs_build(preL+1, preL+llen,inL, i-1);		//根节点的左子树
	}
	if(rlen!=0){
		tree[root].r = dfs_build(preR-rlen+1, preR, i+1, inR);
	}
	return root;
}

1020(25:中-后序,建树+层次遍历)

(1)题目
中-后序,建树+层次遍历
(2)代码

#include<cstdio>
#include<vector>
#include<queue>
using namespace std;


const int N=50;
typedef struct Node{
	int data;
	int l,r;
	Node(){ l = r =-1;}
}Node;

Node tree[N];
int ROOT;

vector<int> post,in;
 
int dfs_build(int inL, int inR, int postL, int postR){	//初始:0 到 n-1
	int root = postR;		//此子树的根节点在post中的postR位置
	tree[root].data = post[postR];
	int i=0;				//此子树的根节点在in中的i位置
	while(in[i]!=post[root]){
		i++;
	} //结束:in[i] == post[root]
	int llen = i - inL;		// (i-1) - inL +1 
	int rlen = inR - i;			// inR - (i+1) +1
	if(llen!=0){
		tree[root].l = dfs_build(inL, i-1, postL, llen+postL-1);		//根节点的左子树
	}
	if(rlen!=0){
		tree[root].r = dfs_build(i+1, inR, postR-rlen, postR-1);
	}
	return root;
}


void bfs(int root){
	queue<int> Q;
	Q.push(root);
	while(!Q.empty()){
		int f = Q.front();
		Q.pop();
		if(f!=ROOT){
			printf(" ");
		}
		printf("%d", tree[f].data);
		if(tree[f].l!=-1){
			Q.push(tree[f].l);
		}
		if(tree[f].r!=-1){
			Q.push(tree[f].r);
		}
	}
}

int main(){
    //freopen("in.txt","r", stdin);

	int n;
	scanf("%d", &n);
	for(int i=0; i<n; i++){
		int t;
		scanf("%d", &t);
		post.push_back(t);
	}
	for(int i=0; i<n; i++){
		int t;
		scanf("%d", &t);
		in.push_back(t);
	}
	int root;
	root = dfs_build(0, n-1, 0, n-1);
	ROOT = root;
	bfs(root);

    //fclose(stdin);
    return 0;
}

1127(30:中-后序,建树+层次遍历)

(1)题目
中-后序:build树,层次遍历(交替从左到右,从右到左)

(2)代码

#include<cstdio>
#include<vector>
#include<queue>

using namespace std;

const int N=50+10;

typedef struct Node{
	int data;
	int l,r;
	Node(){	l = r = -1;}
}Node;

Node tree[N];
int ROOT;

vector<int> in, post;

int dfs_build(int inL, int inR, int postL, int postR){	//初始:0 到 n-1
	int root = postR;		//此子树的根节点在post中的postR位置
	tree[root].data = post[postR];
	int i=0;				//此子树的根节点在in中的i位置
	while(in[i]!=post[root]){
		i++;
	} //结束:in[i] == post[root]
	int llen = i - inL;		// (i-1) - inL +1 
	int rlen = inR - i;			// inR - (i+1) +1
	if(llen!=0){
		tree[root].l = dfs_build(inL, i-1, postL, llen+postL-1);		//根节点的左子树
	}
	if(rlen!=0){
		tree[root].r = dfs_build(i+1, inR, postR-rlen, postR-1);
	}
	return root;
}
	
void bfs(int root){
	queue<int> Q;
	Q.push(root);
	int flag=0;		//标志每层的输出方向
	vector<int> vect;		//一层的node
	while(!Q.empty()){
		int size = Q.size();
		// Q中为同一层
		for(int i=0; i<size; i++){
			int f = Q.front();
			Q.pop();
			vect.push_back(f);
			if(tree[f].l!=-1){
				Q.push(tree[f].l);
			}
			if(tree[f].r!=-1){
				Q.push(tree[f].r);
			}
		}
		//
		if(flag==0){		//从右到左
			for(int i=vect.size()-1; i>=0; i--){
				if(vect[i]!=ROOT){
					printf(" ");
				}
				printf("%d", tree[vect[i]].data);
			}
			flag=1;
		}else{	//从左到右
			for(int i=0; i<vect.size(); i++){
				if(vect[i]!=ROOT){
					printf(" ");
				}
				printf("%d", tree[vect[i]].data);
			}
			flag=0;
		}
		vect.clear();
	}
}

int main(){
    //freopen("in.txt","r", stdin);

	int n;
	scanf("%d", &n);
	for(int i=0; i<n; i++){
		int t;
		scanf("%d", &t);
		in.push_back(t);
	}
	for(int i=0; i<n; i++){
		int t;
		scanf("%d", &t);
		post.push_back(t);
	}
	int root;
	root = dfs_build(0, n-1, 0, n-1);
	ROOT = root;
	bfs(root);

    //fclose(stdin);
    return 0;
}

1138(25:前-中序,求后序的第一个)【非满分】

(1)题目
前-中序:求后序第一个

(2)代码

#include<cstdio>
#include<vector>
using namespace std;

vector<int> pre, in;

bool flag=false;		//标志是否是第一个,提前结束dfs,防止超时
void dfs(int preL, int preR, int inL, int inR){	//初始:0 到 n-1
	if(inL>inR || flag==true){	return;}
	int root = preL;		//此子树的根节点在pre中的preL位置
	int i=0;				//此子树的根节点在in中的i位置
	while(in[i]!=pre[root]){
		i++;
	}						//结束:in[i] == pre[root]
	int llen = i - inL;		// (i-1) - inL +1 
	int rlen = inR - i;			// inR - (i+1) +1
	if(llen!=0){
		dfs(preL+1, preL+llen,inL, i-1);		//根节点的左子树
	}
	if(rlen!=0){
		dfs(preR-rlen+1, preR, i+1, inR);
	}
	if(flag==false){			//不能省if条件 flag==false
		printf("%d", pre[root]);
		flag=true;
	}
}


int main(){
    //freopen("in.txt","r", stdin);

	int n;
	scanf("%d", &n);
	for(int i=0; i<n; i++){
		int t;
		scanf("%d", &t);
		pre.push_back(t);
	}
	for(int i=0; i<n; i++){
		int t;
		scanf("%d", &t);
		in.push_back(t);
	}
	dfs(0, n-1, 0, n-1);

    //fclose(stdin);
    return 0;
}

(3)小结

  • 当N很大时=50000,dfs必须要提前结束,否则很容易超时

1130(25:表达式树,中序输出)

(1)题目
表达式树,中序输出

(2)代码

#include<cstdio>
#include<string>
#include<iostream>

using namespace std;


const int N=50;
typedef struct Node{
	string data;
	int l,r;
	Node(){ l = r =-1;}
}Node;

Node tree[N];
int ROOT;

string dfs(int root){
	if(tree[root].l==-1 && tree[root].r==-1) return tree[root].data;
	if(tree[root].l!=-1 && tree[root].r!=-1) return "("+ dfs(tree[root].l) + tree[root].data + dfs(tree[root].r) + ")";
	if(tree[root].l==-1 && tree[root].r!=-1) return "(" + tree[root].data + dfs(tree[root].r) + ")";
}


int main(){
	//freopen("in.txt","r", stdin);

	int n;
	scanf("%d", &n);
	// 寻找root
	bool haveFather[N] = {false};
	//
	for(int i=1; i<=n; i++){
		cin>>tree[i].data;
		scanf("%d%d", &tree[i].l, &tree[i].r);		
		if(tree[i].l !=-1){
			haveFather[tree[i].l] = true;
		}
		if(tree[i].r !=-1){
			haveFather[tree[i].r] = true;
		}
	}
	int root=1;
	while(haveFather[root]){
		root++;
	}
	ROOT = root;
	string result;
	result = dfs(root);
	//result = result.substr(1, result.length()-2);		//没有考虑到 只有一个结点(数字)的情况
	if(result[0]=='(') result = result.substr(1, result.length()-2);
	printf("%s", result.c_str());

    //fclose(stdin);
    return 0;
}

(3)小结

  • dfs()如何写:
    dfs递归拼接:
    “(” + 左子树 + 根 + 右⼦树 + “)”
    分情况讨论:
    1.左右子树都空(叶子节点), 返回 根 【非符号结点】
    2.左空 右不空,返回 “(” + 根 + 右⼦树 + “)” 【单目运算,如 -b】
    3.左不空 右空,这种情况不不存在
    4.左右都不空,返回 “(” + 左⼦子树 + 根 + 右⼦子树 + “)”
  • string dfs():将整个return结果存在string中,最后处理

1102(逆置:层次遍历+中序遍历)

(1)题目
层次遍历+中序遍历,都倒着输出

(2)代码

#include<cstdio>
#include<vector>
#include<string>
#include<iostream>
#include<queue>

using namespace std;

const int N=50; 

typedef struct Node{
	int data;
	int l, r;
	Node(){ l = r = -1;}
}Node;

vector<Node> tree;		//N=10^5,很大
int ROOT;


vector<int> in;

void dfs(int root){
	if(root==-1){
		return;
	}
	dfs(tree[root].l);
	in.push_back(root);
	dfs(tree[root].r);
}

void bfs(int root){
	queue<int> Q;
	Q.push(root);
	while(!Q.empty()){
		int size = Q.size();
		for(int i=0; i<size; i++){
			int f = Q.front();
			Q.pop();
			if(f!=ROOT){
				printf(" ");
			}
			printf("%d", f);
			if(tree[f].r!=-1){		//先存右子树,再存左子树
				Q.push(tree[f].r);
			}
			if(tree[f].l!=-1){
				Q.push(tree[f].l);
			}
		}
	}
}


int main(){
	freopen("in.txt", "r", stdin);

	int n;
	scanf("%d", &n);
	tree.resize(n);
	bool haveFather[N] = {false};
	for(int i=0; i<n; i++){
		string s1,s2;
		cin>>s1>>s2;
		if(s1!="-"){
			tree[i].l = stoi(s1);
			haveFather[stoi(s1)] = true;
		}
		if(s2!="-"){
			tree[i].r = stoi(s2);
			haveFather[stoi(s2)] = true;
		}
	}
	int root=0;
	while(haveFather[root]){
		root++;
	}
	ROOT = root;
	bfs(root);
	printf("\n");
	dfs(root);
	for(int i=in.size()-1; i>=0; i--){
		if(i!=in.size()-1){
			printf(" ");
		}
		printf("%d", in[i]);
	}


	fclose(stdin);
	return 0;
}

3 完全二叉树

模板

已知n,则可建树

#include<cstdio>
#include<vector>

using namespace std;

const int N=1000+10;

typedef struct Node{
	int data;
	int l, r;
	Node(){ l = r = -1;}
}Node;

vector<Node> tree;		
int ROOT;

//建立完全二叉
void create_comple(int n){
	int lastNoLeaf = n/2;	//最后一个分支结点的序号
	for(int i=1; i<=lastNoLeaf; i++){
		if(2*i <= n){
			tree[i].l = 2*i;
		}
		if((2*i+1) <= n){
			tree[i].r = 2*i+1;
		}		
	}
}

int main(){
	//freopen("in.txt", "r", stdin);

	int n;
	scanf("%d", &n);
	tree.resize(n+1);	//建立完全二叉,从1~n,root=1
	create_comple(n);
	ROOT = 1;

	//fclose(stdin);
	return 0;
}

1110(25:判断完全二叉树)

(1)题目
判断是否是一颗完全二叉树

(2)代码

#include<cstdio>
#include<string>
#include<iostream>
#include<queue>

using namespace std;


const int N=100;

typedef struct Node{
	int data;
	int l,r;
	Node(){l=r=-1;}
}Node;

Node tree[N];
int n;
int ROOT;

int maxIndex=-1;
int last=-1;
void dfs(int root, int index){
	if(root==-1) return;
	if(index > maxIndex) {
		maxIndex=index;
		last = root;
	}
	dfs(tree[root].l, index*2);
	dfs(tree[root].r, index*2+1);
}

int main(){
	//freopen("in.txt", "r",stdin);

	scanf("%d", &n);
	string s1,s2;
	for(int i=0; i<n; i++){		
		cin>>s1>>s2;
		if(s1!="-"){
			tree[i].l = stoi(s1);
		}
		if(s2!="-"){
			tree[i].r = stoi(s2);
		}
	}
	int visit[N];
	fill(visit, visit+N, 0);
	for(int i=0; i<n; i++){
		if(tree[i].l!=-1) visit[tree[i].l]=1;
		if(tree[i].r!=-1) visit[tree[i].r]=1;
	}
	int root=-1;
	for(int i=0; i<n; i++){
		if(visit[i]==0){
			root=i;
			break;
		}
	}
	ROOT=root;

	dfs(ROOT,1);
	if(maxIndex>n){
		printf("NO %d", ROOT);
	}else{
		printf("YES %d", last);
	}

	//fclose(stdin);
	return 0;
}


4 二叉搜索树 / 二叉排序树BST

模板

(1)根据一个数组a[ ] 建树

int insert(int root, int val){
	if(root==-1){
		tree[num].data = val;
		root = num;
		num++;
		return root;
	}
	if(val < tree[root].data){		// 插左子树
		tree[root].l = insert(tree[root].l, val);
	}else{		// 揷右子树
		tree[root].r = insert(tree[root].r, val);
	}
	return root;
}

int root=-1;
for(int i=0; i<k; i++){
	root = insert(root, a[i]);
}

(2)已建树,将 a[ ]填充data(sort+中序遍历)

#include<cstdio>
#include<vector>
#include<algorithm>

using namespace std;

const int N=100+10;

typedef struct Node{
	int data;
	int l, r;
	Node(){ l = r = -1;}
}Node;

vector<Node> tree;		
int ROOT;

int a[N];
int n;
int index=0;

void inOrder(int root){
	if(root==-1) return;
	inOrder(tree[root].l);
	tree[root].data = a[index];
	index++;
	inOrder(tree[root].r);
} 


int main(){
	//freopen("in.txt", "r", stdin);

	scanf("%d", &n);
	tree.resize(n);
	for(int i=0; i<n; i++){
		scanf("%d%d", &tree[i].l, &tree[i].r);
	}
	ROOT = 0;
	//
	for(int i=0; i<n; i++){
		scanf("%d", &a[i]);
	}
	sort(a,a+n);
	inOrder(ROOT);

	//fclose(stdin);
	return 0;
}

1043(25:根据a[ ]建树 + 交换左右子树)

(1)题目
根据a[ ]建树 + 交换左右子树

(2)代码

#include<cstdio>
#include<vector>

using namespace std;

const int N=1000+10;

typedef struct Node{
	int data;
	int l, r;
	Node(){ l = r = -1;}
}Node;

vector<Node> tree;		//N=10^5,很大
int num;
int ROOT;

vector<int> pre;


void create(int root, int key, int father, int lr){
	if(root==-1){
		tree[num].data = key;		
		if(lr==0){
			tree[father].l = num;
		}else{
			tree[father].r = num;
		}
		num++;
		return;
	}
	if(key < tree[root].data ){
		create(tree[root].l, key, root, 0);
	}else{
		create(tree[root].r, key, root, 1);
	}
}

void mirror(int root){		//从最下开始交换左右,只能用后序
	if(root==-1) return;
	mirror(tree[root].l);
	mirror(tree[root].r);
	int temp  = tree[root].l;
	tree[root].l = tree[root].r;
	tree[root].r = temp;
}

void preOrder(int root){
	if(root==-1) return;
	pre.push_back(tree[root].data);
	preOrder(tree[root].l);
	preOrder(tree[root].r);
}

int flag = false;
void postOrder(int root){
	if(root==-1) return;	
	postOrder(tree[root].l);
	postOrder(tree[root].r);
	if(flag!=false){
		printf(" ");		
	}
	printf("%d", tree[root].data);
	flag = true;
}

bool isSame(int a[], int n){
	bool isPre = true;
	for(int i=0; i<n; i++){
		if(a[i]!=pre[i]){
			isPre = false;
			break;
		}
	}
	return isPre;
}

int main(){
	//freopen("in.txt", "r", stdin);

	int n;
	scanf("%d", &n);
	tree.resize(n);
	int a[N];
	int root=0;
	for(int i=0; i<n; i++){
		scanf("%d", &a[i]);
		if(i==0){
			tree[0].data = a[i];		//第一个数就是根节点
			num=1;
		}else{
			create(root, a[i], -1, 0);
		}		
	}	
	ROOT = root;
	//
	preOrder(ROOT);
	if(isSame(a, n)){
		printf("YES\n");
		postOrder(ROOT);
	}else{
		mirror(ROOT);
		pre.clear();
		preOrder(ROOT);
		if(isSame(a, n)){
			printf("YES\n");
			postOrder(ROOT);
		}else{
			printf("NO");
		}
	}

	//fclose(stdin);
	return 0;
}

(3)小结

  • 二叉排序树:给出一组数字(无序)建树
    根节点就是第一个元素
  • 二叉排序树的镜像树
    树:左子树 < root <= 右子树
    镜像树:左子树 >= root > 右子树
  • 若需要从下到上,则后序
    如,交换左右子树

1099(30:先建树,再填充data)

(1)题目
建树:填充data

(2)代码

#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>

using namespace std;

const int N=100+10;

typedef struct Node{
	int data;
	int l, r;
	Node(){ l = r = -1;}
}Node;

vector<Node> tree;		
int ROOT;

int a[N];
int n;
int index=0;

void inOrder(int root){
	if(root==-1) return;
	inOrder(tree[root].l);
	tree[root].data = a[index];
	index++;
	inOrder(tree[root].r);
} 

void bfs(int root){
	queue<int> Q;
	Q.push(root);
	while(!Q.empty()){
		int f = Q.front();
		Q.pop();
		if(f!=ROOT){
			printf(" ");
		}
		printf("%d", tree[f].data);
		if(tree[f].l!=-1){
			Q.push(tree[f].l);
		}
		if(tree[f].r!=-1){
			Q.push(tree[f].r);
		}
	}
}


int main(){
	//freopen("in.txt", "r", stdin);

	scanf("%d", &n);
	tree.resize(n);
	for(int i=0; i<n; i++){
		scanf("%d%d", &tree[i].l, &tree[i].r);
	}
	ROOT = 0;
	//
	for(int i=0; i<n; i++){
		scanf("%d", &a[i]);
	}
	sort(a,a+n);
	inOrder(ROOT);
	bfs(ROOT);

	//fclose(stdin);
	return 0;
}

(3)小结

  • 已建树,填充data:先将a[]排序,再中序填充

1115(30:根据a[ ]建树)

(1)题目
根据数组a[ ]建树

(2)代码

#include<cstdio>
#include<vector>
#include<queue>

using namespace std;

const int N=1000+10;

typedef struct Node{
	int data;
	int l, r;
	Node(){ l = r = -1;}
}Node;

vector<Node> tree;		//N=10^5,很大
int num;
int ROOT;


void create(int root, int key, int father, int lr){
	if(root==-1){
		tree[num].data = key;		
		if(lr==0){
			tree[father].l = num;
		}else{
			tree[father].r = num;
		}
		num++;
		return;
	}
	if(key <= tree[root].data ){
		create(tree[root].l, key, root, 0);
	}else{
		create(tree[root].r, key, root, 1);
	}
}

int level_num[N];
int depth;

void bfs(int root){
	queue<int> Q;
	Q.push(root);
	int level=1;
	while(!Q.empty()){
		int size = Q.size();
		if(level>depth){
			depth = level;
		}
		for(int i=0; i<size; i++){
			level_num[level]++;
			int f = Q.front();
			Q.pop();
			if(tree[f].l!=-1){
				Q.push(tree[f].l);
			}
			if(tree[f].r!=-1){
				Q.push(tree[f].r);
			}
		}
		level++;
	}
}


int main(){
	//freopen("in.txt", "r", stdin);

	int n;
	scanf("%d", &n);
	tree.resize(n);
	int a[N];
	int root=0;
	for(int i=0; i<n; i++){
		scanf("%d", &a[i]);
		if(i==0){
			tree[0].data = a[i];		//第一个数就是根节点
			num=1;
		}else{
			create(root, a[i], -1, 0);
		}		
	}	
	ROOT = root;
	//
	bfs(ROOT);
	if(depth<2){
		printf("%d + %d = %d", 1, 0, 1);
	}else{
		printf("%d + %d = %d", level_num[depth], level_num[depth-1], level_num[depth]+level_num[depth-1]);
	}


	//fclose(stdin);
	return 0;
}

(3)小结

  • 看清题目:左子树是 < 、<=、>=、>哪一种

1064(30:完全二叉搜索树,先建树,再填充data)

(1)题目

完全二叉搜索树,先建树,再填充data

(2)代码

#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>

using namespace std;

const int N=1000+10;

typedef struct Node{
	int data;
	int l, r;
	Node(){ l = r = -1;}
}Node;

vector<Node> tree;		
int ROOT;

int a[N];
int index=0;

//建立完全二叉
void create_comple(int n){
	int lastNoLeaf = n/2;	//最后一个分支结点的序号
	for(int i=1; i<=lastNoLeaf; i++){
		if(2*i <= n){
			tree[i].l = 2*i;
		}
		if((2*i+1) <= n){
			tree[i].r = 2*i+1;
		}		
	}
}

void inOrder(int root){
	if(root==-1) return;
	inOrder(tree[root].l);
	tree[root].data = a[index];
	index++;
	inOrder(tree[root].r);
} 


void bfs(int root){
	queue<int> Q;
	Q.push(root);
	while(!Q.empty()){		
		int f = Q.front();
		Q.pop();
		if(f!=ROOT){
			printf(" ");
		}
		printf("%d", tree[f].data);

		if(tree[f].l!=-1){
			Q.push(tree[f].l);
		}
		if(tree[f].r!=-1){
			Q.push(tree[f].r);
		}
	}
}


int main(){
	//freopen("in.txt", "r", stdin);

	int n;
	scanf("%d", &n);
	tree.resize(n+1);	//建立完全二叉,从1~n,root=1
	create_comple(n);
	ROOT = 1;
	// 将a[]排序,中序填充data
	for(int i=0; i<n; i++){
		scanf("%d", &a[i]);
	}	
	sort(a,a+n);
	inOrder(ROOT);
	bfs(ROOT);

	//fclose(stdin);
	return 0;
}

(3)小结

  • 已知n,就可建立一个唯一的完全二叉树

5 二叉平衡树AVL

模板

建AVL树

typedef struct Node{
	int data;
	int l,r;
	Node(){ l=r=-1;}
}Node;

Node tree[N];
int ROOT;
int num=0;

int L(int root){
	int temp = tree[root].r;
	tree[root].r = tree[temp].l;
	tree[temp].l = root;
	return temp;
}

int R(int root){
	int temp = tree[root].l;
	tree[root].l = tree[temp].r;
	tree[temp].r = root;
	return temp;
}

int LL(int root){
	int temp = R(root);
	return temp;
}

int RR(int root){
	int temp = L(root);
	return temp;
}

int LR(int root){
	tree[root].l = L(tree[root].l);
	int temp = R(root);
	return temp;
}

int RL(int root){
	tree[root].r = R(tree[root].r);
	int temp = L(root);
	return temp;
}

int getH(int root){
	if(root==-1) return 0;
	return max(getH(tree[root].l), getH(tree[root].r))+1;
}

int insert(int root, int val){
	if(root==-1){
		tree[num].data = val;
		root = num;
		num++;
		return root;
	}
	if(val < tree[root].data){		// 插左子树
		tree[root].l = insert(tree[root].l, val);
		//
		if(getH(tree[root].l) - getH(tree[root].r)==2){
			if(val < tree[tree[root].l].data){   // LL
				root = LL(root);
			}else{		// LR
				root = LR(root);
			}		
		}
	}else{		// 揷右子树
		tree[root].r = insert(tree[root].r, val);
		//
		if(getH(tree[root].l) - getH(tree[root].r) == -2){
			if(val < tree[tree[root].r].data){   // RL
				root = RL(root);
			}else{		// RR
				root = RR(root);
			}	
		}
	}
	return root;
}

1066(25:根据a[ ]建AVL树,求root)

(1)题目
根据a[ ]建AVL树,求root

(2)代码

#include<cstdio>
#include<algorithm>

using namespace std;

const int N=1000;

typedef struct Node{
	int data;
	int l,r;
	Node(){ l=r=-1;}
}Node;

Node tree[N];
int ROOT;
int num=0;

int L(int root){
	int temp = tree[root].r;
	tree[root].r = tree[temp].l;
	tree[temp].l = root;
	return temp;
}

int R(int root){
	int temp = tree[root].l;
	tree[root].l = tree[temp].r;
	tree[temp].r = root;
	return temp;
}

int LL(int root){
	int temp = R(root);
	return temp;
}

int RR(int root){
	int temp = L(root);
	return temp;
}

int LR(int root){
	tree[root].l = L(tree[root].l);
	int temp = R(root);
	return temp;
}

int RL(int root){
	tree[root].r = R(tree[root].r);
	int temp = L(root);
	return temp;
}

int getH(int root){
	if(root==-1) return 0;
	return max(getH(tree[root].l), getH(tree[root].r))+1;
}

int insert(int root, int val){
	if(root==-1){
		tree[num].data = val;
		root = num;
		num++;
		return root;
	}
	if(val < tree[root].data){		// 插左子树
		tree[root].l = insert(tree[root].l, val);
		//
		if(getH(tree[root].l) - getH(tree[root].r)==2){
			if(val < tree[tree[root].l].data){   // LL
				root = LL(root);
			}else{		// LR
				root = LR(root);
			}		
		}
	}else{		// 揷右子树
		tree[root].r = insert(tree[root].r, val);
		//
		if(getH(tree[root].l) - getH(tree[root].r) == -2){
			if(val < tree[tree[root].r].data){   // RL
				root = RL(root);
			}else{		// RR
				root = RR(root);
			}	
		}
	}
	return root;
}

int main(){
	//freopen("in.txt", "r",stdin);

	int n;
	scanf("%d", &n);
	int root=-1;
	for(int i=0; i<n; i++){
		int t;
		scanf("%d", &t);
		root = insert(root, t);
	}
	printf("%d", tree[root].data);
	
	
	//fclose(stdin);
	return 0;
}

(3)小结

  • 平衡二叉树一定是二叉排序树,因此中序遍历也是有序的

6 堆

模板

  • 堆是一颗完全二叉树

1147(30:判断是否是一个堆+完全二叉树)

(1)题目
判断是否是一个堆+完全二叉树

(2)代码

#include<cstdio>
#include<vector>

using namespace std;

const int N=1000+10;

typedef struct Node{
	int data;
	int l, r;
	Node(){ l = r = -1;}
}Node;

vector<Node> tree;		
int ROOT;


int n;

//建立完全二叉
void create_comple(int n){
	int lastNoLeaf = n/2;	//最后一个分支结点的序号
	for(int i=1; i<=lastNoLeaf; i++){
		if(2*i <= n){
			tree[i].l = 2*i;
		}
		if((2*i+1) <= n){
			tree[i].r = 2*i+1;
		}		
	}
}


bool isMax = true;
void test_max_heap(int root){
	if(root==-1 || isMax==false) return;
	if(tree[root].l!=-1){
		if(tree[root].data < tree[tree[root].l].data){
			isMax = false;
			return;
		}
		test_max_heap(tree[root].l);
	} 
	if(tree[root].r!=-1){
		if(tree[root].data < tree[tree[root].r].data){
			isMax = false;
			return;
		}
		test_max_heap(tree[root].r);
	}
}

bool isMin = true;
void test_min_heap(int root){
	if(root==-1 || isMin==false) return;
	if(tree[root].l!=-1){
		if(tree[root].data > tree[tree[root].l].data){
			isMin = false;
			return;
		}
	}
	if(tree[root].r!=-1){
		if(tree[root].data > tree[tree[root].r].data){
			isMin = false;
			return;
		}
	}
	test_min_heap(tree[root].l);
	test_min_heap(tree[root].r);

}

int first=1;
void postOrder(int root){
	if(root==-1) return;
	postOrder(tree[root].l);
	postOrder(tree[root].r);
	if(first!=1){
		printf(" ");
	}
	printf("%d", tree[root].data);
	first=0;
}

int main(){
	//freopen("in.txt", "r", stdin);

	int m;
	scanf("%d%d", &m, &n);
	tree.resize(n+1);	//建立完全二叉,从1~n,root=1
	create_comple(n);
	for(int i=0; i<m; i++){
		for(int j=1; j<=n; j++){
			scanf("%d", &tree[j].data);
		}
		ROOT = 1;
		test_max_heap(ROOT);
		if(isMax){
			printf("Max Heap\n");
		}else{
			test_min_heap(ROOT);
			if(isMin){
				printf("Min Heap\n");
			}else{
				printf("Not Heap\n");
			}
		}
		postOrder(ROOT);
		isMax = true;
		isMin = true;
		first=1;
		printf("\n");
	}	

	//fclose(stdin);
	return 0;
}

(3)小结

  • 先根据n构建完全二叉树,再填充data

1155(30:判断是否是一个堆 + 完全二叉树 + 路径)

(1)题目
判断是否是一个堆 + 完全二叉树 + 路径

(2)代码

#include<cstdio>
#include<vector>

using namespace std;

const int N=1000+10;

typedef struct Node{
	int data;
	int l, r;
	Node(){ l = r = -1;}
}Node;

vector<Node> tree;		
int ROOT;


int n;

//建立完全二叉
void create_comple(int n){
	int lastNoLeaf = n/2;	//最后一个分支结点的序号
	for(int i=1; i<=lastNoLeaf; i++){
		if(2*i <= n){
			tree[i].l = 2*i;
		}
		if((2*i+1) <= n){
			tree[i].r = 2*i+1;
		}		
	}
}

int level_node[N];		//路径上,第i层结点的值
int depth;

void output(){
	for(int i=1; i<=depth; i++){
		if(i!=1) printf(" ");
		printf("%d", level_node[i]);
	}
	printf("\n");
}

int isMax = true;
void test_max_heap(){
	if(isMax==false) return;
	for(int i=1; i<=depth-1; i++){
		if(level_node[i] < level_node[i+1]){
			isMax = false;
			break;
		}
	}
}

int isMin = true;
void test_min_heap(){
	if(isMin==false) return;
	for(int i=1; i<=depth-1; i++){
		if(level_node[i] > level_node[i+1]){
			isMin = false;
			break;
		}
	}
}

void dfs(int root, int level){
	if(root==-1) return;
	if(tree[root].l==-1 && tree[root].r==-1){
		level_node[level] = tree[root].data;
		depth = level;
		output();
		test_max_heap();
		test_min_heap();
		return;
	}
	level_node[level] = tree[root].data;
	depth = level;
	dfs(tree[root].r, level+1);
	dfs(tree[root].l, level+1);
} 


int main(){
	//freopen("in.txt", "r", stdin);

	scanf("%d",&n);
	tree.resize(n+1);	//建立完全二叉,从1~n,root=1
	create_comple(n);
	for(int i=1; i<=n; i++){
		scanf("%d", &tree[i].data);
	}
	ROOT = 1;
	dfs(ROOT, 1);
	if(isMax){
		printf("Max Heap\n");
	}else{
		if(isMin){
			printf("Min Heap\n");
		}else{
			printf("Not Heap\n");
		}
	}

	//fclose(stdin);
	return 0;
}

7 并查集

模板

  • 理解:
    一个大集合,可以根据…条件拆分成多个集合
    多棵树在一个father[]中
    多个图在一个father[]中(判断图中有几个连通分支(或:至少添加多少条path可将其变为连通图))
#include<cstdio>

using namespace std;

const int N=1000+10;

int father[N];		//father[i] = j: i的父节点是j,不一定是根节点
// 初始化:father[i] = i

int isRoot[N] = {0};	
//isRoot[i]=j:i是否为根节点(isRoot[i]!=0) or 根节点为i的集合中个数=j



// 查找x的根节点
int findFather(int x){
	int a=x;
	while(x!=father[x]) x = father[x];		//结束后,x为根节点
	// 路径压缩(不写,可能超时)
	while(a!=father[a]){
		int t=a;
		a = father[a];
		father[t] = x;
	}
	return x;
}

// 查找+合并在a集合中
void unionElem(int a, int b){
	int ra = findFather(a);	//a所在集合的根节点
	int rb = findFather(b);		
	if(ra!=rb){		//当不在同一集合中时,合并集合
		father[rb] = ra;		
	}
}

int main(){
	//freopen("in.txt", "r", stdin);

	int n;
	scanf("%d", &n);
	for(int i=1; i<N; i++){
		father[i] = i;		// 初始化
	}
	...
	

	//fclose(stdin);
	return 0;
}

1118(25:判断多少个集合(树),总共多少个元素(鸟),两个元素是否为一个集合)

(1)题目
判断多少个集合(树),总共多少个元素(鸟),两个元素是否为一个集合

(2)代码

#include<cstdio>

using namespace std;

const int N=10000+10;


int father[N];		//father[i] = j: i的父节点是j,不一定是根节点
// 初始化:father[i] = i

int isRoot[N] = {0};		//isRoot[i]=j:i是否为根节点(isRoot[i]!=0)或根节点为i的集合中个数=j

int visit[N]={0};			//输入中是否出现

// 查找x的根节点
int findFather(int x){
	int a=x;
	while(x!=father[x]) x = father[x];		//结束后,x为根节点
	// 路径压缩(不写,可能超时)
	while(a!=father[a]){
		int t=a;
		a = father[a];
		father[t] = x;
	}
	return x;
}

// 查找+合并在a集合中
void unionElem(int a, int b){
	int ra = findFather(a);	//a所在集合的根节点
	int rb = findFather(b);		
	if(ra!=rb){		//当不在同一集合中时,合并集合
		father[rb] = ra;
	}
}


int main(){
	//freopen("in.txt", "r", stdin);

	int n;
	scanf("%d", &n);
	for(int i=0; i<=N; i++){
		father[i] = i;		// 初始化
	}
	//
    int first, t,k;
    for(int i = 0; i < n; i++) {
        scanf("%d%d", &k, &first);
        visit[first] = 1;
        for(int j = 0; j < k-1; j++) {
            scanf("%d", &t);
            unionElem(first, t);
            visit[t] = 1;
        }
    }
    for(int i = 1; i <= N; i++) {
        if(visit[i] == 1) {
            int r = findFather(i);
            isRoot[r]++;
        }
    }
	//
	int tree_num=0, bird_num=0;
	for(int i=1; i<=N; i++){
		if(isRoot[i]!=0){		//i为根节点
			tree_num++;
			bird_num += isRoot[i];
		}
	}
	printf("%d %d\n", tree_num, bird_num);
	int m;
	scanf("%d", &m);
	for(int j=0; j<m; j++){
		int b1,b2;
		scanf("%d%d", &b1, &b2);
		int r1 = findFather(b1);
		int r2 = findFather(b2);
		if(r1==r2){
			printf("Yes\n");
		}else{
			printf("No\n");
		}
	}

	

	//fclose(stdin);
	return 0;
}

(3)小结

  • pat平台:可以输入一个,输出一个,不用先存储输入
  • int visit[N] 是否输入过i

1107(30:每个人有各种爱好,按相同爱好划分集合)

(1)题目
每个人有各种爱好,按相同爱好划分集合

(2)代码

#include<cstdio>
#include<vector>
#include<algorithm>
#include<functional>

using namespace std;

const int N=1000+10;

int father[N];		//father[i] = j: i的根节点是j
// 初始化:father[i] = i

int isRoot[N] = {0};		//isRoot[i]=j:根节点为i的集合中成员个数


// 查找x的根节点
int findFather(int x){
	int a=x;
	while(x!=father[x]) x = father[x];		//结束后,x为根节点
	// 路径压缩(不写,可能超时)
	while(a!=father[a]){
		int t=a;
		a = father[a];
		father[t] = x;
	}
	return x;
}

// 查找+合并在a集合中
void unionElem(int a, int b){
	int ra = findFather(a);	//a所在集合的根节点
	int rb = findFather(b);		
	if(ra!=rb){		//当不在同一集合中时,合并集合
		father[rb] = ra;		
	}
}

int main(){
	//freopen("in.txt", "r", stdin);

	int n;
	scanf("%d", &n);
	for(int i=1; i<=n; i++){
		father[i] = i;		// 初始化
	}
	int hobby[N] = {0};		//hobby[i] = j:i为hobby编号,j为人的编号(第一个喜欢i的人)
	//
	for(int i=1; i<=n; i++){	//编号为i的人
		int k;
		scanf("%d:", &k);
		for(int j=0; j<k; j++){	
			int h;
			scanf("%d", &h);	//编号为h的hobby
			if(hobby[h]==0){	//还没有人喜欢hobby:h
				hobby[h] = i;
			}
			unionElem(hobby[h], i);	//第一个喜欢hobby[h]的人 和 i 合并
		}
	}
	//	统计根节点为i的集合大小:isRoot[i]
	for(int i=1; i<=n; i++){
		int r = findFather(i);			// r = father[i];	错误!!!!!
		isRoot[r]++;
	}
	//
	vector<int> vect;
	for(int i=1; i<=n; i++){
		if(isRoot[i]!=0){
			vect.push_back(isRoot[i]);
		}
	}
	sort(vect.begin(), vect.end(), greater<int>());
	printf("%d\n", vect.size());
	for(int i=0; i<vect.size(); i++){
		if(i!=0) printf(" ");
		printf("%d", vect[i]);
	}

	//fclose(stdin);
	return 0;
}

分析:
在这里插入图片描述
(3)小结

  • 先写出模板:findFather(),unionElem()
  • 如何设计并查集?
    (1)father[]里存什么?
    输出:每个集合(同一hobby)的人数
    因此,设计:father[]里存人的编号
    (2)合并什么?
    编号 i 的人喜欢hobby :2
    unionElem(第一个喜欢hobby:2的人的编号, i)
  • father[N]数组不一定指向根节点,必须用findFather()
  • 数组大括号赋值,只能赋0
    int hobby[N] = {-1}; 失败
  • hobby[N] 相当于A1118,A1114里的visit[N]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值