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 Si is the index of the supplier for the i-th member. Sroot 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]