一、⼆叉树的概念
1. 二叉树的定义
注意:⼆叉树结点的两个孩⼦,⼀个被称为左孩⼦,⼀个被称为右孩⼦。其顺序是固定的,就像⼈ 的左⼿和右⼿,不能颠倒混淆。
2. 特殊的⼆叉树
(1)满⼆叉树
(2)完全⼆叉树
对⼀棵树有 个结点的⼆叉树按层序编号,所有的结点的编号从 样深度的满⼆叉树的编号为从 1 ∼n 1 ∼n 。如果这棵树所有结点和同 比特就业课 的结点位置相同,则这棵⼆叉树为完全⼆叉树。
注意:要从后往前依次删除!!!
二、⼆叉树的存储
1. 顺序存储
2. 链式存储
案例:
题⽬描述: 有⼀个 n(n ≤ 10^6 ) 个结点的⼆叉树。给出每个结点的两个⼦结点编号(均不超过n ),建⽴⼀棵⼆叉树(根节点的编号为1 ),如果是叶⼦结点,则输⼊ 0 0。
输⼊描述:
第⼀⾏⼀个整数 n ,表⽰结点数。
之后 n⾏,第 i ⾏两个整数 l 、 r ,分别表⽰结点 i 的左右⼦结点编号。若 l = 0 则表⽰⽆左⼦结点, r = 0 同理。
代码实现:
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int n;
int l[N], r[N];
int main()
{
cin >> n;
// 存二叉树
for(int i = 1; i <= n; i++)
{
cin >> l[i] >> r[i];
}
return 0;
}
三、 ⼆叉树的遍历
1. 深度优先遍历
案例:
题⽬描述:
有⼀个 n(n ≤ 10^6 ) 个结点的⼆叉树。给出每个结点的两个⼦结点编号(均不超过 n ),建⽴⼀棵⼆叉树(根节点的编号为1 ),如果是叶⼦结点,则输⼊ 0 0。
输⼊描述:
第⼀⾏⼀个整数 n ,表⽰结点数。之后 n ⾏,第 i ⾏两个整数 l 、 r ,分别表⽰结点 i 的左右⼦结点编号。若 l = 0m则表⽰⽆左⼦结点, r = 0 同理。
测试⼀: 4 0 2 3 4 0 0 0 0 测试⼆: 2 2 0 0 0 测试三: 3 2 3 0 0 0 0 测试四: 7 2 3 0 4 5 6 0 0 0 0 7 0 0 0
代码实现:
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int n;
int l[N], r[N]; // 存树
// 先序遍历
void dfs1(int u)
{
cout << u << " ";
if(l[u]) dfs1(l[u]);
if(r[u]) dfs1(r[u]);
}
// 中序遍历
void dfs2(int u)
{
if(l[u]) dfs2(l[u]);
cout << u << " ";
if(r[u]) dfs2(r[u]);
}
// 后序遍历
void dfs3(int u)
{
if(l[u]) dfs3(l[u]);
if(r[u]) dfs3(r[u]);
cout << u << " ";
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> l[i] >> r[i];
}
dfs1(1); // 先序遍历
cout << endl;
dfs2(1); // 中序遍历
cout << endl;
dfs3(1); // 后序遍历
cout << endl;
return 0;
}
2. 宽度优先遍历
这个就和常规的树的遍历⽅式⼀样,直接⽤队列帮助层序遍历即可。
#include <iostream>
#include <queue>
using namespace std;
const int N = 1e6 + 10;
int n;
int l[N], r[N];
void bfs()
{
queue<int> q;
q.push(1);
while(q.size())
{
int u = q.front(); q.pop();
cout << u << " ";
if(l[u]) q.push(l[u]);
if(r[u]) q.push(r[u]);
}
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> l[i] >> r[i];
}
bfs();
return 0;
}