数据结构实验5:二叉树的应用

目录

一、实验目的

二、实验原理

1. 基本概念

2. 基本操作

2.1 二叉数的定义

2.2 二叉树的建立

2.2.1 创建新节点

2.2.2 建立二叉树

2.3 二叉树的遍历

2.3.1 先序遍历(NLR)

2.3.2 中序遍历(LNR)

2.3.3 后序遍历(LRN)

2.3.4 层次遍历

2.4 二叉树的节点个数统计

2.5 二叉树的深度计算

三、实验内容

问题描述

代码

截图


一、实验目的

1、掌握二叉树的定义;

2.掌握二叉树的基本操作,如二叉树的建立、遍历、结点个数统计、树的深度计

算等。

二、实验原理

1. 基本概念

  1. 节点(Node): 二叉树的基本单元是节点。每个节点包含一个数据元素和指向左子节点和右子节点的指针。

  2. 根节点(Root): 树的顶部节点称为根节点。每个二叉树只有一个根节点。

  3. 叶节点(Leaf): 没有子节点的节点称为叶节点,也叫终端节点。它们是树结构的末端。

  4. 父节点(Parent)和子节点(Child): 一个节点的直接上级是其父节点,而直接下级是其子节点。每个节点最多有一个父节点,但可以有零个、一个或两个子节点。

  5. 子树(Subtree): 由一个节点及其所有后代节点组成的树称为子树。

  6. 深度(Depth): 一个节点的深度是指从根节点到该节点的路径的长度。根节点的深度为0。

  7. 高度(Height): 一棵树的高度是指树中任意节点的最大深度。也就是说,树的高度是从根节点到最深叶节点的最长路径。

  8. 层次(Level): 一棵树的层次是指树中的节点分布在哪一层。根节点在第一层,其子节点在第二层,以此类推。

  9. 二叉搜索树(Binary Search Tree,BST): 是一种特殊的二叉树,其中每个节点的左子树中的所有节点的值都小于该节点的值,而右子树中的所有节点的值都大于该节点的值。

  10. 遍历(Traversal): 访问二叉树中所有节点的一种方式。常见的遍历方式有前序遍历、中序遍历和后序遍历。

2. 基本操作

2.1 二叉数的定义

  1. 空二叉树(Empty Binary Tree): 一个二叉树可以是空的,即不包含任何节点。

  2. 非空二叉树(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)。这种遍历方法通常使用队列来实现,确保每一层的节点按顺序被访问。

  1. 初始化: 创建一个队列,并将根节点入队。

  2. 循环直到队列为空:

    • 从队列中取出一个节点,访问该节点。
    • 如果该节点有左子节点,将左子节点入队。
    • 如果该节点有右子节点,将右子节点入队。
  3. 重复步骤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
#include 
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& 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 path; // 用于存储路径的数组
    // 调用函数进行构建并输出路径
    buildTreeAndPrintPaths(root, path);
    return 0;
}

截图

 

你可能感兴趣的:(数据结构,数据结构,算法)