二叉树的概念
二叉树是一种特殊的树形结构,它的每个结点至多只有两棵子树(也有可能没有孩子或者只有一个孩子),并且二叉树的子树有左右之分,其次序不能颠倒,因此是一颗有序树。特殊的,如果一棵二叉树的所有非叶子结点都存在左右孩子,并且所有叶子结点都在同一层上,那么这棵二叉树就称为满二叉树。如果在满二叉树的基础上,在最后一层叶子结点上从右往左依次删除若干个节点,剩下的就是一棵完全二叉树。二叉树还存在以下的性质:对于下标为i的节点,父节点下标为i/2,左孩子下标为i×2,右孩子下标为i×2+1。
二叉树的存储
在上一章中,我们已经介绍了树的存储。同样,二叉树也是可以用vector数组或者是链式前向星来存储,仅需在存储过程中标记左右孩子即可。但是,由于二叉树的特殊性质,我们还可以用顺序存储和链式存储来存储二叉树。
顺序存储
顺序结构存储就是用数组来存储。根据二叉树的性质,我们可以从根节点出发,按照层序遍历的顺序,按照编号依次存放在数组对应下标的位置上。顺序结构存储一般只用于存储满二叉树或者完全二叉树,否则会导致空间利用率不高。
链式存储
我们创建两个数组l[N]和r[N],其中l[i]表示节点为i的左孩子编号,r[i]表示节点为i的右孩子编号。代码如下:
#include <iostream>
using namespace std;
const int N = 1e5 + 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;
}
二叉树的遍历
深度优先遍历
不同于常规树的深度优先遍历,二叉树因其独特的性质可以分成三种:先序遍历,中序遍历以及后序遍历。这三种遍历方式的不同在于处理根节点的时机。对于一棵二叉树而言,整体可以划分成三部分:根节点、左子树和右子树。
先序遍历:根→左→右
中序遍历:左→根→右
后序遍历:左→右→根
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n;
int l[N], r[N];
void dfs1(int p)
{
if(p == 0)
return;
cout << p << " ";
dfs1(l[p]);
dfs1(r[p]);
}
void dfs2(int p)
{
if(p == 0)
return;
dfs2(l[p]);
cout << p << " ";
dfs2(r[p]);
}
void dfs3(int p)
{
if(p == 0)
return;
dfs3(l[p]);
dfs3(r[p]);
cout << p << " ";
}
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;
}
宽度优先遍历
和常规的树的遍历方式一样,直接借助队列层序遍历即可。
#include <iostream>
#include <queue>
using namespace std;
const int N = 1e5 + 10;
int n;
int l[N], r[N];
void bfs()
{
queue<int> q;
q.push(1);
while(q.size())
{
auto t = q.top();
q.pop();
cout << t << " ";
if(l[t])
q.push(l[t]);
if(r[t])
q.push(r[t]);
}
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> l[i] >> r[i];
}
bfs();
return 0;
}

306

被折叠的 条评论
为什么被折叠?



