目录
一、实验目的
1、掌握二叉树的定义;
2.掌握二叉树的基本操作,如二叉树的建立、遍历、结点个数统计、树的深度计
算等。
二、实验原理
1. 基本概念
节点(Node): 二叉树的基本单元是节点。每个节点包含一个数据元素和指向左子节点和右子节点的指针。
根节点(Root): 树的顶部节点称为根节点。每个二叉树只有一个根节点。
叶节点(Leaf): 没有子节点的节点称为叶节点,也叫终端节点。它们是树结构的末端。
父节点(Parent)和子节点(Child): 一个节点的直接上级是其父节点,而直接下级是其子节点。每个节点最多有一个父节点,但可以有零个、一个或两个子节点。
子树(Subtree): 由一个节点及其所有后代节点组成的树称为子树。
深度(Depth): 一个节点的深度是指从根节点到该节点的路径的长度。根节点的深度为0。
高度(Height): 一棵树的高度是指树中任意节点的最大深度。也就是说,树的高度是从根节点到最深叶节点的最长路径。
层次(Level): 一棵树的层次是指树中的节点分布在哪一层。根节点在第一层,其子节点在第二层,以此类推。
二叉搜索树(Binary Search Tree,BST): 是一种特殊的二叉树,其中每个节点的左子树中的所有节点的值都小于该节点的值,而右子树中的所有节点的值都大于该节点的值。
遍历(Traversal): 访问二叉树中所有节点的一种方式。常见的遍历方式有前序遍历、中序遍历和后序遍历。
2. 基本操作
2.1 二叉数的定义
空二叉树(Empty Binary Tree): 一个二叉树可以是空的,即不包含任何节点。
非空二叉树(Non-empty Binary Tree): 一个非空二叉树包含一个根节点,以及分别为左子树和右子树的两个二叉树。
- 根节点包含一个数据元素。
- 左子树和右子树都是二叉树,可以是空二叉树或非空二叉树
struct TreeNode {
int data;
struct TreeNode* left;//左孩子
struct TreeNode* right;//右孩子
};
2.2 二叉树的建立
2.2.1 创建新节点
分配内存,且左孩子和右孩子指针置为NULL
struct TreeNode* createNode(int elem) {
struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
if (newNode == NULL) {//未分配成功
cout << "内存分配错误" << endl;
exit(EXIT_FAILURE);
}
newNode->data = elem;
newNode->left = NULL;
newNode->right = NULL;
return newNode;//返回新节点
}
2.2.2 建立二叉树
递归建立
struct TreeNode* buildTree() {
int elem;
cout << "输入节点的数值(-1代表空节点):";
cin >> elem;
if (elem == -1) {//输入的是空节点
return NULL;
}
struct TreeNode* root = createNode(elem);//创建新节点
cout << "输入" << elem << "的左节点" << endl;
root->left = buildTree();
cout << "输入" << elem << "的右节点" << endl;
root->right = buildTree();
return root;
}
2.3 二叉树的遍历
2.3.1 先序遍历(NLR)
其遍历顺序为先访问根节点,然后递归地先序遍历左子树,最后递归地先序遍历右子树。
void preOrderTraversal(struct TreeNode* root) {
if (root != NULL) {
cout << root->data << " ";//访问节点
preOrderTraversal(root->left);//访问左孩子
preOrderTraversal(root->right);//访问右孩子
}
}
2.3.2 中序遍历(LNR)
其遍历顺序为先递归地中序遍历左子树,然后访问根节点,最后递归地中序遍历右子树。
void inOrderTraversal(struct TreeNode* root) {
if (root != NULL) {
inOrderTraversal(root->left);//访问左孩子
cout << root->data << " ";//访问节点
inOrderTraversal(root->right);//访问右孩子
}
}
2.3.3 后序遍历(LRN)
其遍历顺序为先递归后序遍历左子树,然后访问右子树,最后递归地后序遍历根节点。
void postOrderTraversal(struct TreeNode* root) {
if (root != NULL) {
postOrderTraversal(root->left);//访问左孩子
postOrderTraversal(root->right);//访问右孩子
cout << root->data << " ";//访问节点
}
}
2.3.4 层次遍历
层次遍历是一种按照树的层级顺序逐层访问节点的遍历方式,也称为广度优先遍历(Breadth-First Traversal)。这种遍历方法通常使用队列来实现,确保每一层的节点按顺序被访问。
初始化: 创建一个队列,并将根节点入队。
循环直到队列为空:
- 从队列中取出一个节点,访问该节点。
- 如果该节点有左子节点,将左子节点入队。
- 如果该节点有右子节点,将右子节点入队。
重复步骤2,直到队列为空。
struct TreeNode {
int data;
struct TreeNode* left;//左孩子
struct TreeNode* right;//右孩子
};
// 定义队列节点
typedef struct QueueNode {
struct TreeNode* treeNode;//二叉树节点的指针
struct QueueNode* next;//下一个节点的指针
} QueueNode;
// 定义队列
typedef struct Queue {
QueueNode* front;//队列的前端
QueueNode* rear;//队列的后端
} Queue;
// 初始化队列
void initializeQueue(Queue* queue) {
queue->front = queue->rear = NULL;
}
// 入队
void enqueue(Queue* queue, TreeNode* treeNode) {
//创建一个新的队列节点
QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
newNode->treeNode = treeNode;
newNode->next = NULL;
if (queue->rear == NULL) {//如果队列为空,则此节点为第一个节点
queue->front = queue->rear = newNode;
}
else {
queue->rear->next = newNode;
queue->rear = newNode;
}
}
// 出队
TreeNode* dequeue(Queue* queue) {
if (queue->front == NULL) {
return NULL; // 空队列
}
TreeNode* treeNode = queue->front->treeNode;
QueueNode* temp = queue->front;
queue->front = queue->front->next;
free(temp);
if (queue->front == NULL) {//如果是空队列
queue->rear = NULL;
}
return treeNode;
}
// 层次遍历
void levelOrderTraversal(TreeNode* root) {
if (root == NULL) {
return; // 空树
}
Queue queue;
initializeQueue(&queue);//初始化队列
enqueue(&queue, root);//入队
while (queue.front != NULL) {//当队列不为空
TreeNode* current = dequeue(&queue);//当前出队的节点
cout << current->data<<" ";
if (current->left != NULL) {//若左子树不为空
enqueue(&queue, current->left);
}
if (current->right != NULL) {//若右子树不为空
enqueue(&queue, current->right);
}
}
}
2.4 二叉树的节点个数统计
采用递归的方式进行统计
int countNodes(TreeNode* root) {
if (root == NULL) {//如果为空节点
return 0;
}
else {
return 1 + countNodes(root->left) + countNodes(root->right);
}
}
2.5 二叉树的深度计算
采用递归的方式进行计算
int calculateDepth(TreeNode* root) {
if (root == NULL) {
return 0;
}
else {
int leftDepth = calculateDepth(root->left);//左子树的深度
int rightDepth = calculateDepth(root->right);//右子树的深度
if (leftDepth > rightDepth) {//如果左子树更深
return 1 + leftDepth;
}
else {
return 1 + rightDepth;
}
}
}
三、实验内容
问题描述
1.以二叉链表表示二叉树,建立一棵二叉树(算法 5.3);
2.输出二叉树的中序遍历结果(算法 5.1);
3.输出二叉树的前序遍历结果(见讲稿);
4.输出二叉树的后序遍历结果(见讲稿);
5.计算二叉树的深度(算法 5.5);
6.统计二叉树的结点个数(算法 5.6);
7.统计二叉树的叶结点个数;
8.统计二叉树的度为 1 的结点个数;
9.输出二叉树中从每个叶子结点到根结点的路径。
代码
#include<iostream>
#include <vector>
using namespace std;
struct TreeNode {
int data;
struct TreeNode* left;//左孩子
struct TreeNode* right;//右孩子
};
//建立节点
struct TreeNode* createNode(int elem) {
struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
if (newNode == NULL) {//未分配成功
cout << "内存分配错误" << endl;
exit(EXIT_FAILURE);
}
newNode->data = elem;
newNode->left = NULL;
newNode->right = NULL;
return newNode;//返回新节点
}
//构建二叉树
struct TreeNode* buildTree() {
int elem;
cout << "输入节点的数值(-1代表空节点):";
cin >> elem;
if (elem == -1) {//输入的是空节点
return NULL;
}
struct TreeNode* root = createNode(elem);//创建新节点
cout << "输入" << elem << "的左节点" << endl;
root->left = buildTree();
cout << "输入" << elem << "的右节点" << endl;
root->right = buildTree();
return root;
}
//NLR
void preOrderTraversal(struct TreeNode* root) {
if (root != NULL) {
cout << root->data << " ";//访问节点
preOrderTraversal(root->left);//访问左孩子
preOrderTraversal(root->right);//访问右孩子
}
}
//LNR
void inOrderTraversal(struct TreeNode* root) {
if (root != NULL) {
inOrderTraversal(root->left);//访问左孩子
cout << root->data << " ";//访问节点
inOrderTraversal(root->right);//访问右孩子
}
}
//LRN
void postOrderTraversal(struct TreeNode* root) {
if (root != NULL) {
postOrderTraversal(root->left);//访问左孩子
postOrderTraversal(root->right);//访问右孩子
cout << root->data << " ";//访问节点
}
}
//统计节点数
int countNodes(TreeNode* root) {
if (root == NULL) {//如果为空节点
return 0;
}
else {
return 1 + countNodes(root->left) + countNodes(root->right);
}
}
//统计叶节点个数
//实质统计左右子树均为空的节点
int countLeafNodes(TreeNode* root) {
if (root == NULL) {//如果为空节点
return 0;
}
else {
if (root->left == NULL && root->right == NULL) {
return 1 + countLeafNodes(root->left) + countLeafNodes(root->right);
}
else {
return countLeafNodes(root->left) + countLeafNodes(root->right);
}
}
}
//统计单节点个数
int countOneNodes(TreeNode* root) {
if (root == NULL) {//如果为空节点
return 0;
}
else {
if (((root->left == NULL) && (root->right!=NULL))||((root->right == NULL) && (root->left != NULL)) ) {//如果是单节点
return 1 + countOneNodes(root->left) + countOneNodes(root->right);
}
else {
return countOneNodes(root->left) + countOneNodes(root->right);
}
}
}
//计算深度
int calculateDepth(TreeNode* root) {
if (root == NULL) {
return 0;
}
else {
int leftDepth = calculateDepth(root->left);//左子树的深度
int rightDepth = calculateDepth(root->right);//右子树的深度
if (leftDepth > rightDepth) {//如果左子树更深
return 1 + leftDepth;
}
else {
return 1 + rightDepth;
}
}
}
// 构建二叉树,并输出从叶子节点到根节点的路径
void buildTreeAndPrintPaths(TreeNode* root, vector<int>& path) {
if (root == nullptr) {
return;
}
// 将当前节点加入路径
path.push_back(root->data);
// 如果是叶子节点,打印路径
if (root->left == nullptr && root->right == nullptr) {
cout << "Path from leaf to root: ";
for (int i = path.size() - 1; i >= 0; --i) {
cout << path[i] << " ";
}
cout << endl;
}
// 递归处理左子树和右子树
buildTreeAndPrintPaths(root->left, path);
buildTreeAndPrintPaths(root->right, path);
// 在返回之前,将当前节点从路径中移除
path.pop_back();
}
int main() {
struct TreeNode* root=buildTree();//建立二叉树
cout << endl <<"中序遍历的结果为:";
inOrderTraversal(root);
cout << endl << "先序遍历的结果为:";
preOrderTraversal(root);
cout << endl << "后序遍历的结果为:";
postOrderTraversal(root);
cout << endl << "二叉树的深度为:" << calculateDepth(root);
cout << endl << "二叉树的节点个数为:" << countNodes(root);
cout << endl << "二叉树的叶子节点个数为:" << countLeafNodes(root);
cout << endl << "二叉树中度为1的节点个数为:" << countOneNodes(root);
vector<int> path; // 用于存储路径的数组
// 调用函数进行构建并输出路径
buildTreeAndPrintPaths(root, path);
return 0;
}
截图