概述
1.树是一种非线性的数据结构,他表现的是一对多,是由n(n>=0)个节点组成的有限集,n=0的树成为空树。
2.有且仅有一个根节点,且根节点无前驱节点。
3.每个节点至多仅有一个父节点和若干个儿子节点,每个子树不相交
4.若一个树有n个节点,那么他有n-1个边(即节点之间的连接部分)
二叉树
正如它的名字名字一样,他的每个节点至多有两个分支(即两个儿子),所以就有了以一般二叉树、完全二叉树和满二叉树的概念。
完全二叉树:除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布
遍历完全二叉树
由于有了二叉树备辈分的关系和至多有两个分叉的特点,我们一般将两个分叉节点成为左右儿子
树的遍历分为三类:前序、中序、后序、层级遍历
先序遍历
中序遍历
后序遍历
层级遍历:这个相比就不用多说了,就是从树的最顶层依次向下从左向右依次遍历。
例题
给你一棵 n 个节点的完全二叉树,节点的编号为 1 到 n,二叉树的根为1号节点。编号为 i (1≤i≤n) 的节点的左儿子如果存在的话,编号为 i+i;编号为 i (1≤i≤n) 的节点的右儿子如果存在的话,编号为 i+i+1。
现在请你求出这棵完全二叉树的先序、中序和后序遍历的结果。
#include<iostream>
using namespace std;
int n;
void preorder(int t)//先序
{
cout << t << " ";
if (t + t <= n)
preorder(t + t);
if (t + t + 1 <= n)
preorder(t + t + 1);
}
void inorder(int t)//中序
{
if (t + t <= n)
inorder(t + t);
cout << t << " ";
if (t + t + 1 <= n)
inorder(t + t + 1);
}
void postorder(int t)//后序
{
if (t + t <= n)
postorder(t + t);
if (t + t + 1 <= n)
postorder(t + t + 1);
cout << t << " ";
}
int main()
{
cin >> n;
preorder(1);
cout << endl;
inorder(1);
cout << endl;
postorder(1);
return 0;
}
树的直径
树的直径是这棵二叉树上最长且中间不重复不相交的一条路径
例题
给你一棵树,让你求出这棵树的直径长度(直径经过的边的数量)。 树以下列方式给出:
- 输入第一行给出一个数 n,表示一共有 n 个节点;
- 接下来 n−1 行,每行给出两个数 x,y(x!=y),表示x,y 之间有一条边。
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
int n, pre[100001], c[100001], l, q[100001], frong = 1, rear = 0, dist[100001];
vector<int>edges[100001];
void dfs(int x)//深度优先搜索
{
for (auto y : edges[x]) {//从当前节点向四周探索
if (y != pre[x]) {
pre[y] = x;
dist[y] = dist[x] + 1;//多走一条路+1
dfs(y);
}
}
}
int main()
{
int n;
cin >> n;
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
edges[x].push_back(y);//与x相邻的数y
edges[y].push_back(x);//与y相邻的数x
}
memset(dist, 0, (sizeof(dist)));//重置数组
memset(pre, 0, sizeof(pre));
pre[1] = -2;//这个等于几都行反正特殊点
dfs(1);//从根节点开始,直径肯定经过了这个节点
int idx = 0, v = 0;
for (int i = 1; i <= n; i++)
if (dist[i] > v)
v = dist[i], idx = i;
memset(dist, 0, (sizeof(dist)));//重置数组
memset(pre, 0, sizeof(pre));
pre[idx] = -2;
dfs(idx);//距离根节点最远那个节点开始探索
v = 0;
for (int i = 1; i <= n; i++)//挨个节点找
v = max(v, dist[i]);
cout << v;
return 0;
}
树的共同祖先(LAC)
字面意思,无需多言
例题:
describe:
给你一棵 n 个节点的二叉树,节点的编号为 1到 n,二叉树的根为 1号节点。
读入 u,v,请求出 u 号节点和 v 号节点的最近公共祖先(Lowest Common Ancestor)。
如果 x 号节点既是 u 号节点的祖先也是 v 号节点的祖先,则称 x 号节点是 u 号节点和 v 号节点的公共祖先。
如果 x 号节点是 u 号节点和 v 号节点的所有公共祖先中深度最深的,则称 x 号节点是 u 号节点和 v 号节点的最近公共祖先。
input:
第一行一个整数 n 表示节点数。
接下来 n 行,每行两个整数,第一个整数表示 i 号节点的左儿子的编号,第二个整数表示 i 号节点的右儿子的编号,如果某个数字为 0 表示没有对应的子节点。
输入保证是一棵二叉树。
最后一行两个整数 u,v 表示要求最近公共祖先的两个节点的编号。
对于所有数据,保证2≤n≤1000,1≤u,v≤n。
#include<iostream>
using namespace std;
struct node {
int l, r, fa;//l左儿子、r右儿子、fa表示父亲节点
int depth;//深度
}arr[10001];
int queue[10000];//队列
int main()
{
int n, x, y;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> x >> y;
if (x)
arr[i].l = x, arr[x].fa = i;
if (y)
arr[i].r = y, arr[y].fa = i;
}
int front = 1, rear = 1;
queue[1] = 1;
arr[1].depth = 1;
while (front <= rear) {
int p = queue[front];
++front;
if (arr[p].l) {//若左儿子存在
queue[++rear] = arr[p].l;
arr[arr[p].l].depth = arr[p].depth + 1;
}
if (arr[p].r) {
queue[++rear] = arr[p].r;
arr[arr[p].r].depth = arr[p].depth + 1;
}
}
int z,c;
cin >> z >> c;
if (arr[z].depth < arr[c].depth)
swap(z, c);//达成同一层
int cut = arr[z].depth - arr[c].depth;
for (int i = 1; i <= cut; i++) {
z = arr[z].fa;
}
while (z != c)
{
z = arr[z].fa;//向上找祖先,直到找到一样的
c = arr[c].fa;
}
cout << z;
return 0;
}
树上路径
例题
给你一棵树和树上的两个节点 u,v(u!=v), 需要你求出从 u 到v 的路径经过的所有节点。
树以下列方式给出:
- 输入第一行给出一个数n,表示一共有 n 个节点;
- 接下来n−1 行,每行给出两个数x,y(x!=y),表示x,y 之间有一条边。
输入最后一行两个数u,v 表示询问。
#include<iostream>
using namespace std;
#include<vector>
int n, pre[100001], c[100001], l, q[100001], front = 1, rear = 0;
vector<int>edges[1000001];
void dfs(int x)//深度优先搜索
{
for (auto y : edges[x]) {
if (y != pre[x]) {
pre[y] = x;
dfs(y);
}
}
}
void bfs(int u)//宽度优先搜索
{
q[++rear] = u;
while (front <= rear) {
int x = q[front];
front++;
for (auto y : edges[x]) {//遍历所有与x相邻的点
if (y != pre[x]) {
pre[y] = x;//y从x过来,从y中找到它的上级
q[++rear] = y;
}
}
}
}
int main()
{
cin >> n;
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
edges[x].push_back(y);//相连的数放在对应数组中
edges[y].push_back(x);
}
int u, v;
cin >> u >> v;
pre[u] = -1;//第一个数的位置
bfs(u);
l = 0;
for (int i = v; i != u; i = pre[i])//反着找
c[++l] = i;
c[++l] = u;
for (int i = l; i; --i)
printf("%d ", c[i]);
return 0;
}