二叉树基本操作

目录

0. 二叉树节点结构体

1前序创建二叉树

2. 统计度为0的节点个数 

3. 统计度为1的节点个数

4. 统计度为2的节点个数

5. 统计二叉树的节点个数

6. 计算二叉树的深度

7. 二叉树的层序遍历(一次性输出)

8. 二叉树的层序打印(每层输出)

9. 判断二叉树是否为满二叉树

10. 判断二叉树是否为完全二叉树

 11. 全部代码


0. 二叉树节点结构体

// 定义二叉树的节点结构
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} // 构造函数
};

这段代码定义了二叉树的节点结构 `TreeNode`,具体分析如下:

- `struct TreeNode { ... };`:这行代码定义了一个名为 `TreeNode` 的结构体,表示二叉树的节点。结构体中包含了三个成员变量:`int val` 表示节点的值,`TreeNode* left` 和 `TreeNode* right` 分别表示指向左子节点和右子节点的指针。

- `TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}`:这是一个构造函数,用于初始化 `TreeNode` 结构体的对象。构造函数接受一个整数参数 `x`,表示节点的值。在构造函数的初始化列表中,将节点的值 `val` 初始化为参数 `x`,并将左右子节点的指针 `left` 和 `right` 初始化为 `nullptr`,表示初始时没有左右子节点。

这样定义的 `TreeNode` 结构体使得每个节点都包含一个整数值和两个指向左右子节点的指针,方便构建二叉树数据结构。

1前序创建二叉树

// 前序创建二叉树
TreeNode* createPreOrder() {
    int val;
    std::cin >> val;
    if (val == 0) {
        return nullptr;
    }
    TreeNode* root = new TreeNode(val);
    root->left = createPreOrder();
    root->right = createPreOrder();
    return root;
}

2. 统计度为0的节点个数 

// 统计度为0的节点个数
int count_Node_0(TreeNode* T) {
    if (T == nullptr) { // 二叉树为空
        return 0;
    }
    if ((T->left == nullptr) && (T->right == nullptr)) { // 找到叶子节点
        return count_Node_0(T->left) + count_Node_0(T->right) + 1;
    }
    else {
        return count_Node_0(T->left) + count_Node_0(T->right);
    }
}

这段代码实现了统计二叉树中度为0的节点个数的功能。具体分析如下:

- `int count_Node_0(TreeNode* T) { ... }`:这是一个名为 `count_Node_0` 的函数,接受一个指向二叉树根节点的指针 `T`,返回度为0的节点个数。

- `if (T == nullptr) { return 0; }`:首先检查根节点是否为空,如果为空,则表示二叉树为空树,直接返回0。

- `if ((T->left == nullptr) && (T->right == nullptr)) { return count_Node_0(T->left) + count_Node_0(T->right) + 1; }`:然后检查当前节点是否为叶子节点,即左右子节点均为空。如果是叶子节点,则返回1(表示当前节点是度为0的节点),并递归地统计左子树和右子树中度为0的节点个数。

- `else { return count_Node_0(T->left) + count_Node_0(T->right); }`:如果当前节点不是叶子节点,则递归地统计左子树和右子树中度为0的节点个数,并返回它们的和。

        这段代码通过递归遍历二叉树的所有节点,并判断每个节点的度是否为0,从而统计出二叉树中度为0的节点个数。

3. 统计度为1的节点个数

// 统计度为1的节点个数
int count_Node_1(TreeNode* T) {
    if (T == nullptr) {
        return 0;
    }
    if (((T->left == nullptr)&&(T->right != nullptr)) || ((T->left != nullptr) && (T->right == nullptr))) { // 找到度为1的节点
        return count_Node_1(T->left) + count_Node_1(T->right) + 1;
    }
    else {
        return count_Node_1(T->left) + count_Node_1(T->right);
    }
}

4. 统计度为2的节点个数

// 统计度为2的节点个数
int count_Node_2(TreeNode* T) {
    if (T == nullptr) {
        return 0;
    }
    if ((T->left != nullptr) && (T->right != nullptr)) { // 找到度为2的节点
        return count_Node_2(T->left) + count_Node_2(T->right) + 1;
    }
    else {
        return count_Node_2(T->left) + count_Node_2(T->right);
    }
}

5. 统计二叉树的节点个数

// 统计二叉树节点个数
int count_Node(TreeNode* T) {
    if (T == nullptr) {
        return 0;
    }

    return count_Node(T->left) + count_Node(T->right) + 1; // 总节点个数=左子树节点数+右子树节点数+根结点数(1)
}

6. 计算二叉树的深度

// 计算二叉树的深度
int Depth(TreeNode* T) {
    if (T == nullptr) { // 二叉树为空,则深度为0
        return 0;
    }
    else {
        int m = Depth(T->left);
        int n = Depth(T->right);
        if (m > n) {
            return m + 1;
        }
        else {
            return n + 1;
        }
    }
}

7. 二叉树的层序遍历(一次性输出)

// 层序遍历
void level_order_tras(TreeNode* T) {
    if (T == nullptr) { // 代码若改为if (T = nullptr) 则会引出下面两个错误
        return;
    }

    // 创建队列,用于存放待访问的节点
    std::queue<TreeNode*> q;
    // 将根节点入队
    q.push(T);
    // std::cout << T->val; // 报错【测试代码】
    // std::cout << q.front()->val; // 报错【测试代码】

    //开始层序遍历
    while (!q.empty()) {
        // 确定当前层节点个数
        int levelSize = q.size();

        //遍历当前层的所有节点
        for (int i = 0; i < levelSize; ++i) {
            // 取出队首节点
            TreeNode* node = q.front();
            q.pop();
            // 输出节点值
            std::cout << node->val << " ";
            
            // 将当前节点的左右子节点入队
            if (node->left != nullptr) {
                q.push(node->left);
            }
            if (node->right != nullptr) {
                q.push(node->right);
            }
        }
    }
}

8. 二叉树的层序打印(每层输出)

// 层序遍历(每层输出)
void level_print(TreeNode* T) {
    if (T == nullptr) {
        return;
    }

    // 创建队列,用于存放待访问的节点
    std::queue<TreeNode*> q;
    // 将根节点入队
    q.push(T);

    int j = 1; // j表示层数
    //开始层序遍历
    while (!q.empty()) {
        std::cout << "第" << j << "层的数据为:";
        // 确定当前层节点个数
        int levelSize = q.size();

        //遍历当前层的所有节点
        for (int i = 0; i < levelSize; i++) {
            // 取出队首节点
            TreeNode* node = q.front();
            q.pop();
            std::cout << node->val << " ";

            // 将当前节点的左右子节点入队
            if (node->left != nullptr) {
                q.push(node->left);
            }
            if (node->right != nullptr) {
                q.push(node->right);
            }
        }
        j++;
        std::cout << std::endl; // 换行
    }

}

9. 判断二叉树是否为满二叉树

// 判断一个二叉树是否为满二叉树
void Full_binary_tree(TreeNode* T) {
    if (T == nullptr) {
        std::cout << "该二叉树是一颗空二叉树!" << std::endl;
    }
    int h = Depth(T); // 获取二叉树的深度,即层数
    int node = count_Node(T); // 获取二叉树的节点总数
    if (node == (2 ^ h) - 1) { // 说明二叉树为满二叉树
        std::cout << "该二叉树是一颗满二叉树!" << std::endl;
        std::cout << "深度为:" << h << "," << "节点总数为:" << node << std::endl;
    }
    else {
        std::cout << "这颗二叉树不是满二叉树!" << std::endl;
        std::cout << "深度为:" << h << "," << "节点总数为:" << node << std::endl;
    }
   
}

10. 判断二叉树是否为完全二叉树

// 判断是否为完全二叉树

/*
基本思路:使用队列按层次遍历二叉树,遍历过程中将二叉树的所有结点依次入队。
当出队遇见一个NULL结点时,若遍历其后结点都为NULL则为完全二叉树,否则不是完全二叉树。
因为层次遍历完全二叉树时,当遍历到空结点时前面所有非空结点已经被遍历完了,若空结点之后还有非空结点则不是完全二叉树。
*/
bool Complete_binary_tree(TreeNode* T) {
    if (T == nullptr) { 
        std::cout << "该二叉树是一颗空二叉树!" << std::endl;
    }

    // 创建队列,用于存放待访问的节点
    std::queue<TreeNode*> q;
    // 将根节点入队
    q.push(T);
    bool encounteredNull = false; // 是否遇到过空节点

    //开始层序遍历
    while (!q.empty()) { // 队列不为空
        
        // 取出队首节点
        TreeNode* current = q.front();
        q.pop();

        if (current == nullptr) {
            encounteredNull = true;
            continue;
        }

        if (encounteredNull && current != nullptr) {
            // 遇到空节点后面还有非空节点,则说明不是完全二叉树
            return false;
        }
        q.push(current->left);
        q.push(current->right);  
    } 
    return true;
}

 11. 全部代码

        对于普通的二叉树,为了能让每个结点确认是否有左右孩子,我们可以对它进行扩展,也就是将二叉树中每个结点的空指针引出一个“虚结点”,用 0 来代替。

同时,这里我们采用输入前序遍历序列来构造二叉树的方式进行讲解。

例如这样一颗二叉树:

将其空指针用“虚结点”引出后表示为:

其先序遍历序列为:

1 2 0 3 0 0 4 0 0

那么如何创建呢?

我们有这样的规则:

  • 按照输入的序列创建二叉树,分别递归的创建左子树和右子树
  • 如遇到 0 代表子树为空
#include <iostream>
#include <queue>
#include <cmath>

// 定义二叉树的节点结构
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} // 构造函数
};

// 前序创建二叉树
TreeNode* createPreOrder() {
    int val;
    std::cin >> val;
    if (val == 0) {
        return nullptr;
    }
    TreeNode* root = new TreeNode(val);
    root->left = createPreOrder();
    root->right = createPreOrder();
    return root;
}


// 前序遍历打印二叉树
void preOrderTraversal(TreeNode* root) {
    if (root) {
        std::cout << root->val << " ";
        preOrderTraversal(root->left);
        preOrderTraversal(root->right);
    }
}

// 中序遍历打印二叉树
void InOrderTraversal(TreeNode* root) {
    if (root) {
        InOrderTraversal(root->left);
        std::cout << root->val << " ";
        InOrderTraversal(root->right);
    }
}

// 统计度为0的节点个数
int count_Node_0(TreeNode* T) {
    if (T == nullptr) { // 二叉树为空
        return 0;
    }
    if ((T->left == nullptr) && (T->right == nullptr)) { // 找到叶子节点
        return count_Node_0(T->left) + count_Node_0(T->right) + 1;
    }
    else {
        return count_Node_0(T->left) + count_Node_0(T->right);
    }
}

// 统计度为1的节点个数
int count_Node_1(TreeNode* T) {
    if (T == nullptr) {
        return 0;
    }
    if (((T->left == nullptr)&&(T->right != nullptr)) || ((T->left != nullptr) && (T->right == nullptr))) { // 找到度为1的节点
        return count_Node_1(T->left) + count_Node_1(T->right) + 1;
    }
    else {
        return count_Node_1(T->left) + count_Node_1(T->right);
    }
}

// 统计度为2的节点个数
int count_Node_2(TreeNode* T) {
    if (T == nullptr) {
        return 0;
    }
    if ((T->left != nullptr) && (T->right != nullptr)) { // 找到度为2的节点
        return count_Node_2(T->left) + count_Node_2(T->right) + 1;
    }
    else {
        return count_Node_2(T->left) + count_Node_2(T->right);
    }
}

// 统计二叉树节点个数
int count_Node(TreeNode* T) {
    if (T == nullptr) {
        return 0;
    }

    return count_Node(T->left) + count_Node(T->right) + 1; // 总节点个数=左子树节点数+右子树节点数+根结点数(1)
}

// 计算二叉树的深度
int Depth(TreeNode* T) {
    if (T == nullptr) { // 二叉树为空,则深度为0
        return 0;
    }
    else {
        int m = Depth(T->left);
        int n = Depth(T->right);
        if (m > n) {
            return m + 1;
        }
        else {
            return n + 1;
        }
    }
}

// 层序遍历
void level_order_tras(TreeNode* T) {
    if (T == nullptr) { // 代码若改为if (T = nullptr) 则会引出下面两个错误
        return;
    }

    // 创建队列,用于存放待访问的节点
    std::queue<TreeNode*> q;
    // 将根节点入队
    q.push(T);
    // std::cout << T->val; // 报错【测试代码】
    // std::cout << q.front()->val; // 报错【测试代码】

    //开始层序遍历
    while (!q.empty()) {
        // 确定当前层节点个数
        int levelSize = q.size();

        //遍历当前层的所有节点
        for (int i = 0; i < levelSize; ++i) {
            // 取出队首节点
            TreeNode* node = q.front();
            q.pop();
            // 输出节点值
            std::cout << node->val << " ";
            
            // 将当前节点的左右子节点入队
            if (node->left != nullptr) {
                q.push(node->left);
            }
            if (node->right != nullptr) {
                q.push(node->right);
            }
        }
    }
}

// 层序遍历(每层输出)
void level_print(TreeNode* T) {
    if (T == nullptr) {
        return;
    }

    // 创建队列,用于存放待访问的节点
    std::queue<TreeNode*> q;
    // 将根节点入队
    q.push(T);

    int j = 1; // j表示层数
    //开始层序遍历
    while (!q.empty()) {
        std::cout << "第" << j << "层的数据为:";
        // 确定当前层节点个数
        int levelSize = q.size();

        //遍历当前层的所有节点
        for (int i = 0; i < levelSize; i++) {
            // 取出队首节点
            TreeNode* node = q.front();
            q.pop();
            std::cout << node->val << " ";

            // 将当前节点的左右子节点入队
            if (node->left != nullptr) {
                q.push(node->left);
            }
            if (node->right != nullptr) {
                q.push(node->right);
            }
        }
        j++;
        std::cout << std::endl; // 换行
    }

}

/*
// 判断一个二叉树是否为完全二叉树
void Complete_binary_tree(TreeNode* T) { // 【错误代码】
    // 思路:具有n个节点的完全二叉树的深度为{log2n}+1(2为底数,{}表示向下取整 )
    if (T == nullptr) {
        std::cout << "该二叉树是一颗空二叉树!" << std::endl;
    }
    int h = Depth(T); // 获取二叉树的深度,即层数
    int node = count_Node(T); // 获取二叉树的节点总数

    // 使用换底公式定义以2为底数的对数函数
    double log_2_node = log(node) / log(2);// 计算以e为底的对数,即ln(x)
    std::cout << "log_2_node:" << log_2_node;
    if (h == (floor(log_2_node) + 1)) {
        std::cout << "这颗二叉树是一颗完全二叉树!" << std::endl;
    }
    std::cout << "这颗二叉树不是一颗完全二叉树!" << std::endl;
}
*/





// 判断是否为完全二叉树

/*
基本思路:使用队列按层次遍历二叉树,遍历过程中将二叉树的所有结点依次入队。
当出队遇见一个NULL结点时,若遍历其后结点都为NULL则为完全二叉树,否则不是完全二叉树。
因为层次遍历完全二叉树时,当遍历到空结点时前面所有非空结点已经被遍历完了,若空结点之后还有非空结点则不是完全二叉树。
*/
bool Complete_binary_tree(TreeNode* T) {
    if (T == nullptr) { 
        std::cout << "该二叉树是一颗空二叉树!" << std::endl;
    }

    // 创建队列,用于存放待访问的节点
    std::queue<TreeNode*> q;
    // 将根节点入队
    q.push(T);
    bool encounteredNull = false; // 是否遇到过空节点

    //开始层序遍历
    while (!q.empty()) { // 队列不为空
        
        // 取出队首节点
        TreeNode* current = q.front();
        q.pop();

        if (current == nullptr) {
            encounteredNull = true;
            continue;
        }

        if (encounteredNull && current != nullptr) {
            // 遇到空节点后面还有非空节点,则说明不是完全二叉树
            return false;
        }
        q.push(current->left);
        q.push(current->right);  
    } 
    return true;
}

// 判断一个二叉树是否为满二叉树
void Full_binary_tree(TreeNode* T) {
    if (T == nullptr) {
        std::cout << "该二叉树是一颗空二叉树!" << std::endl;
    }
    int h = Depth(T); // 获取二叉树的深度,即层数
    int node = count_Node(T); // 获取二叉树的节点总数
    if (node == (2 ^ h) - 1) { // 说明二叉树为满二叉树
        std::cout << "该二叉树是一颗满二叉树!" << std::endl;
        std::cout << "深度为:" << h << "," << "节点总数为:" << node << std::endl;
    }
    else {
        std::cout << "这颗二叉树不是满二叉树!" << std::endl;
        std::cout << "深度为:" << h << "," << "节点总数为:" << node << std::endl;
    }
   
}


int main() {
    
    // 前序创建二叉树
    std::cout << "Enter the elements of the tree in pre-order traversal (0 for null):\n";
    TreeNode* root = createPreOrder();
    // std::cout << root->val; // 输出1【测试代码】
    
    // 前序遍历二叉树
    std::cout << "Pre-order traversal of the created tree: ";
    preOrderTraversal(root);
    std::cout << std::endl; // 换行

    // 中序遍历二叉树
    std::cout << "In-order traversal of the created tree: ";
    InOrderTraversal(root);
    std::cout << std::endl; // 换行
    
    // 统计度为0的节点个数 
    int Node0_n = count_Node_0(root);
    std::cout << "度为0的节点个数为:" << Node0_n << std::endl;
    
    // 统计度为1的节点个数
    int Node1_n = count_Node_1(root);
    std::cout << "度为1的节点个数为:" << Node1_n << std::endl;

    // 统计度为2的节点个数
    int Node2_n = count_Node_2(root);
    std::cout << "度为2的节点个数为:" << Node2_n << std::endl;

    // 统计二叉树的节点个数
    int Node = count_Node(root);
    std::cout << "节点个数为:" << Node << std::endl;
    
    // 计算二叉树的深度
    int depth = Depth(root);
    std::cout << "二叉树的深度为:" << depth << std::endl;

    // 二叉树的层序遍历(一次性输出)
    std::cout << "二叉树的层序遍历结果如下:" << std::endl;
    level_order_tras(root);
    std::cout << std::endl;

    // 二叉树的层序打印(每层输出)
    std::cout << "二叉树的每层输出结果如下:" << std::endl;
    level_print(root);
    std::cout << std::endl; // 换行

    // 判断二叉树是否为满二叉树
    Full_binary_tree(root);

    //判断二叉树是否为完全二叉树
    if (Complete_binary_tree(root)) {
        std::cout << "这颗二叉树是一颗完全二叉树!" << std::endl;
    }
    else {
        std::cout << "这颗二叉树不是一颗完全二叉树!" << std::endl;
    }

    // 释放内存,防止内存泄漏
    delete root;

    return 0;
}

输出结果: 

  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值