1.简介
树是一种非线性数据结构,相较于队列,链表,树的一个节点可能会生出多个分支。
一般而言,一个树会包含一个根节点,向下延伸出若干个子节点,每个末端的节点被称为叶子节点。
2.有根树
有几个常见的概念:树的根节点,父亲节点,兄弟节点,子节点,祖先,后代(子树),树深度h,节点深度hp。
2.1二叉树
对于一个节点,他拥有的子结点个数成为这个节点的度。节点的度不超过2的树成为二叉树。
两个子节点被称为左右儿子,子树被称为左右子树。
左右儿子甚至根节点本身都有可能缺失(一个节点都没有称为空二叉树)
有相同节点深度的二叉树节点处于同一层。
完全二叉树:除了最后一层外其他层的节点数全满,而且最后一层的节点从左到右排满除直到最后一个节点。
满二叉树:所有层的节点全满。
2.1.1 完全二叉树的存储
完全二叉树由于特性,可以简单地用数组来模拟其结构:
一般会以数组 [1] 位置为根节点建立二叉树;
数组的 [t] 位置的左儿子和右儿子对应的位置分别为 [2t] 和 [2t+1] ,父亲节点的位置为 [t/2] 。
void Build(int t){
updataData(t);//添加数据
Build(t+t);
Build(2t+1);//如果子节点存在
用这种方法建立非完全二叉树,由于需要保证每个子节点的标号是父亲节点的2倍,因此会浪费掉大量的空间。
2.1.2 一般二叉树的存储
可以用数组下标模拟节点编号,用多个数组来记录节点信息。
fa[N], L[N], R[N]
struct TreeNode{
int value;
int l, r, fa;
} a[100001];
//用struct存储
与链表类似,二叉树的父节点、子节点信息也可以用指针来存,同时我们会记录根节点指针。
struct TreeNode{
int value;
TreeNode *l, *r, *fa;
};
TreeNode *root;
2.1.3二叉树基本操作
//新建节点
struct TreeNode{
int value;
TreeNode *l, *r, *fa;//初始为NULL
TreeNode(int x){value = x;}
};
TreeNode *p = new TreeNode(x);
//根节点初始化
TreeNode *root;
root = new TreeNode(v);
//插入子节点
void Insert(TreeNode *fa, TreeNode *p, int flag){
//flag = 0插入左边,=1插入右边
if(!flag)
fa->l = p;
else
fa->r = p;
p->fa = fa;
}
TreeNode *p = new TreeNode(v);
Insert(fa, p, flag);
2.1.4 二叉树的遍历顺序
遍历顺序可分为:先序遍历,中序遍历,后序遍历
//先序遍历(DLR)
void PreOrder(TreeNode *p){
cout << p->value << endl;
if(p->l)PreOrder(p->l);
if(p->r)PreOrder(p->r);
}
PreOrder(root);
//中序遍历(LDR)
void InOrder(TreeNode *p){
if(p->l)InOrder(p->l);
cout << p->value << endl;
if(p->r)InOrder(p->r);
}
InOrder(root);
//后序遍历(LRD)
void PostOrder(TreeNode *p){
if(p->l)InOrder(p->l);
if(p->r)InOrder(p->r);
cout << p->value << endl;
}
InOrder(root);
//层级遍历(BFS序列)
TreeNode *q[N];
void Bfs(TreeNode *root){
int front = 1, rear = 1;
q[1] = root;
while(front <= rear){
TreeNode *p = q[front];
front++;
cout << p->value << endl;
if(p->l) q[++rear] = p->l;
if(p->r) q[++rear] = p->r;
}
}
Bfs(root);
例题1. 遍历一般二叉树
给你一棵 n 个节点的二叉树,节点的编号为 1 到 n,二叉树的根为 1 号节点。请你求出这棵二叉树的先序、中序和后序遍历的结果。
输入格式
第一行一个整数 n 表示节点数。接下来 n 行,每行两个整数,第一个整数表示 i 号节点的左儿子的编号,第二个整数表示 i 号节点的右儿子的编号,如果某个数字为 0 表示没有对应的子节点。
输入保证是一棵二叉树。
输出格式
输出三行,每行 n 个数代表一种遍历的结果。
第一行为先序遍历的结果,第二行为中序遍历的结果,第三行为后序遍历的结果。
数据规模
对于所有数据,保证 1≤n≤1024。
#include<bits/stdc++.h>;
using namespace std;
int n;
struct TreeNode{
int fa;
int l, r;
} a[1025];
inline void preorder(int t){
printf("%d ", t);
if(a[t].l)
preorder(a[t].l);
if(a[t].r)
preorder(a[t].r);
}
inline void inorder(int t){
if(a[t].l)
inorder(a[t].l);
printf("%d ", t);
if(a[t].r)
inorder(a[t].r);
}
inline void postorder(int t){
if(a[t].l)
postorder(a[t].l);
if(a[t].r)
postorder(a[t].r);
printf("%d ", t);
}
int main(){
scanf("%d",&n);
for(int i = 1; i<= n; i++){
int x, y;
scanf("%d%d", &x, &y);
if(x)
a[i].l = x, a[x].fa = i;
if(y)
a[i].r = y, a[y].fa = i;
}
preorder(1);
printf("\n");
inorder(1);
printf("\n");
postorder(1);
}
例题2.二叉树的最近公共祖先
给你一棵 n 个节点的二叉树,节点的编号为 1 到 n,二叉树的根为 1 号节点。读入 u,v ,请求出 u 号节点和 v 号节点的最近公共祖先(Lowest Common Ancestor)。
如果 x 号节点既是 u 号节点的祖先也是 v 号节点的祖先,则称 x 号节点是 u 号节点和 v号节点的公共祖先。
如果 x号节点是 u 号节点和 v 号节点的所有公共祖先中深度最深的,则称 x 号节点是 u 号节点和 v 号节点的最近公共祖先。
输入格式
第一行一个整数 n表示节点数。
接下来 n行,每行两个整数,第一个整数表示 i 号节点的左儿子的编号,第二个整数表示 i 号节点的右儿子的编号,如果某个数字为 0表示没有对应的子节点。
输入保证是一棵二叉树。
最后一行两个整数 u,v表示要求最近公共祖先的两个节点的编号。
输出格式
输出一行一个整数,代表 u号节点和 v 号节点的最近公共祖先。
数据规模:
对于所有数据,保证 2≤n≤1000,1≤u,v≤n。
#include<bits/stdc++.h>;
using namespace std;
struct node{
int l, r, fa;
}a[1001];
int n , c[1001], d[1001];
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
int x, y;
scanf("%d%d", &x, &y);
if(x)
a[i].l = x, a[x].fa = i;
if(y)
a[i].r = y, a[y].fa = i;
}
int u, v;
scanf("%d%d", &u, &v);
int l1 = 0;
while(u!= 1)
c[++l1] = u, u = a[u].fa;
c[++l1] = 1;
int l2 = 0;
while(v!= 1)
d[++l2] = v, v = a[v].fa;
d[++l2] = 1;
int x = 0;
for(int i = l1, j = l2; i && j; --i, --j)
if(c[i] == d[j])
x = c[i];
else
break;
printf("%d", x);
}
例题3.二叉树的子树和1
给你一棵 n 个节点的二叉树,节点的编号为 1 到 n,二叉树的根为 1 号节点。每个节点都有一个权值,i 号节点的权值为 ai,请求出每个节点的子树的权值和(子树内节点的权值的和)。
输入格式
第一行一个整数 n表示节点数。接下来 n行,每行两个整数,第一个整数表示 i 号节点的左儿子的编号,第二个整数表示 i 号节点的右儿子的编号,如果某个数字为 0表示没有对应的子节点。
输入保证是一棵二叉树。
接下来一行 n个整数,第 i 个整数 ai 表示 i号节点的权值。
输出格式
输出一行 n个整数,第 i 个整数表示 i 号节点的子树的权值和。
数据规模
对于所有数据,保证 1≤n≤1000,1≤ai≤100。
#include<bits/stdc++.h>;
using namespace std;
struct node{
int l, r, fa;
int value;
}a[1001];
int n, cnt;
inline void order(int t){
cnt+= a[t].value;
if(a[t].l)
order(a[t].l);
if(a[t].r)
order(a[t].r);
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
int x, y;
scanf("%d%d", &x, &y);
if(x)
a[i].l = x, a[x].fa = i;
if(y)
a[i].r = y, a[y].fa = i;
}
for(int i = 1; i<=n; i++){
scanf("%d", &a[i].value);
}
for(int i = 1; i<=n; i++){
cnt = 0;
order(i);
printf("%d ", cnt);
}
}