1 二叉树
二叉树(Binary Tree)是一种常见的树状数据结构,它由一组节点组成,每个节点最多有两个子节点,分别称为左子节点和右子节点。二叉树具有以下特点:
- 每个节点最多有两个子节点,分别称为左子节点和右子节点。
- 左子树和右子树也是二叉树,它们的结构与父节点类似。
- 二叉树的顺序不固定,可以是任意形状。
两种有特殊形式
二叉树有两种形式:满二叉树和完全二叉树
- 满二叉树:如果该二叉树的所有叶子节点都在最后一层,并且结点总数= 2^n -1,n 为层数,则我们称为满二又树。简单点说,满二叉树的每一个分支都是满的。
- 完全二叉树: 对一个有n个节点的二叉树,按层级顺序编号,则所有节点的编号为从1到n。如果这个树所有节点和同样深度的满二叉树的编号为从1到n的节点位置相同,则这个二叉树为完全二叉树。完全二叉树的条件没有满二叉树那么苛刻: 满二叉树要求所有分支都是满的;而完全二叉树只需保证最后一个节点之前的节点都齐全即可。
2 二叉树节点实现
struct biTree{
// 数据域
int val;
// 指向左孩子的指针域
biTree * left;
// 指向右孩子的指针域
biTree * right;
};
如下图所示:
3 遍历二叉树
二叉树的遍历方式分为三种:前序遍历、中序遍历、后序遍历。
一下是前序遍历的代码:遍历采用递归的方式遍历。在前序遍历中,首先访问根节点,然后按照左子树和右子树的顺序递归地进行前序遍历。
// 线性遍历函数,递归
void preTree(biTree *T){
if (T == NULL){
return;
} else{
// 先遍历中间节点
cout << T->val << endl;
preTree(T->left);
preTree(T->right);
}
}
4 存储结构
在这里我们从下往上开始构建节点,从节点5开始构建。
// 创建节点5
biTree *node5 = new biTree;
node5->val = 5;
node5->left = NULL;
node5->right = NULL;
// 创建节点8
biTree *node8 = new biTree;
node8->val = 8;
node8->left = NULL;
node8->right = NULL;
// 创建节点3
biTree *node3 = new biTree;
node3->val = 3;
node3->left = NULL;
node3->right = NULL;
// 将5,3,8三个连接起来
node3->left = node5;
node3->right = node8;
// 创建2节点
biTree *node2 = new biTree;
node2->val = 2;
node2->left = NULL;
node2->right = NULL;
// 创建节点1
biTree *node1 = new biTree;
node1->val = 1;
node1->left = NULL;
node1->right = NULL;
// 将左子树,右子树和根节点1连接起来
node1->left = node3;
node1->right = node2;
总体代码如下:
#include <iostream>
using namespace std;
struct biTree{
// 数据域
int val;
// 指向左孩子的指针域
biTree * left;
// 指向右孩子的指针域
biTree * right;
};
// 线性遍历函数,递归
void preTree(biTree *T){
if (T == NULL){
return;
} else{
// 先遍历中间节点
cout << T->val << endl;
preTree(T->left);
preTree(T->right);
}
}
int main() {
// 创建节点5
biTree *node5 = new biTree;
node5->val = 5;
node5->left = NULL;
node5->right = NULL;
// 创建节点8
biTree *node8 = new biTree;
node8->val = 8;
node8->left = NULL;
node8->right = NULL;
// 创建节点3
biTree *node3 = new biTree;
node3->val = 3;
node3->left = NULL;
node3->right = NULL;
// 将5,3,8三个连接起来
node3->left = node5;
node3->right = node8;
// 创建二节点
biTree *node2 = new biTree;
node2->val = 2;
node2->left = NULL;
node2->right = NULL;
// 创建节点1
biTree *node1 = new biTree;
node1->val = 1;
node1->left = NULL;
node1->right = NULL;
// 将左子树,右子树和根节点1连接起来
node1->left = node3;
node1->right = node2;
preTree(node1);
cout << endl;
return 0;
}
最终输出为:
5 创建二叉树函数化
由于以上创建二叉树繁琐,我们将其修改成函数。在函数中,需要使用递归方法创建二叉树。代码如下:
// 先序创建二叉树
void creatpre(biTree *&T){
int n;
cout << "请输入当前节点的val值,0代表当前节点为NULL" ;
cin >>n;
if ( n == 0){
T = NULL;
return;
} else{
biTree * node = new biTree;
node->val = n;
T = node;
// 先序遍历创建当前节点的左子树
creatpre(T->left);
// 先序遍历创建这个节点的右子树
creatpre(T->right);
}
}
步骤:
- 输入节点值:将输入的值存储在变量n中,
- 检查是否为NULL节点:如果输入的值为0,那么设置当前节点指针T为NULL,结束递归。
- 创建非NULL节点:如果输入的值是0,则创建新的biTree节点,并将该节点的值设置为输入值n,然后将节点的指针赋值给T。
- 递归创建左子树:递归调用 creatpre 函数来创建当前节点的左子树。
- 递归创建左子树:递归调用 creatpre 函数来创建当前节点的右子树。
改进后的代码如下所示:
#include <iostream>
using namespace std;
struct biTree{
// 数据域
int val;
// 指向左孩子的指针域
biTree * left;
// 指向右孩子的指针域
biTree * right;
};
// 先序创建二叉树
void creatpre(biTree *&T){
int n;
cout << "请输入当前节点的val值,0代表当前节点为NULL" ;
cin >>n;
if ( n == 0){
T = NULL;
return;
} else{
biTree * node = new biTree;
node->val = n;
T = node;
// 先序遍历创建当前节点的左子树
creatpre(T->left);
// 先序遍历创建这个节点的右子树
creatpre(T->right);
}
}
// 线性遍历函数,递归
void preTree(biTree *T){
if (T == NULL){
return;
} else{
// 先遍历中间节点
cout << T->val << endl;
preTree(T->left);
preTree(T->right);
}
}
int main() {
// 创建一个空树
biTree *T = NULL;
// 使用先序创建一个二叉树
creatpre(T);
// 使用先序遍历这个二叉树
preTree(T);
return 0;
}