C++实现二叉树基本操作详解

yizhihongxing

C++实现二叉树基本操作详解

二叉树是计算机科学中的重要数据结构,其实现在C++编程中是必不可少的。本文将从二叉树的定义、基本操作的实现以及示例说明三个方面,详细讲解如何在C++中实现二叉树。

一、二叉树的定义

二叉树是一种树形结构,其中每个节点最多只包含两个子节点(左子节点和右子节点)。每个节点都包含一个值(或者说是一个数据项),而左右子节点则分别指向另外两个节点,或者为nullptr。下图展示了一个典型的二叉树结构:

        1
       / \
      2   3
     / \
    4   5

在上述示例中,节点1是二叉树的根节点,而其左子节点为2、右子节点为3。同样,节点2的左子节点为4、右子节点为5。

二、二叉树的基本操作

1. 插入节点

要向二叉树中插入一个新的节点,首先需要判断这个新节点是在左子树中插入还是在右子树中插入。如果刚好没有该节点,则可以直接插入;如果存在该节点,则需要对该节点进行更新。

下面是向二叉树中插入新节点的示例代码:

// 在二叉搜索树中插入一个新节点
// @param root: 二叉树的根节点
// @param data: 新节点的值
void insert(TreeNode*& root, int data) {
    if(root == nullptr) {
        root = new TreeNode(data);
        return;
    }
    if(data < root->val) {
        insert(root->left, data);
    }
    else if(data > root->val) {
        insert(root->right, data);
    }
    // 如果相等,则说明节点已经存在
}

上述代码中,TreeNode类表示二叉树的节点,其中包含一个整数值val、左子节点left和右子节点right。在insert函数中,首先判断二叉树是否为空,如果是,则将新节点作为根节点。如果二叉树不为空,则从根节点开始,递归地向左子树或右子树中插入新节点。

2. 遍历树

二叉树可以通过三种方式进行遍历:前序遍历、中序遍历和后序遍历。在前序遍历中,首先访问根节点,然后按照左子节点、右子节点的顺序访问下一个节点。在中序遍历中,首先访问左子节点,然后访问根节点,最后访问右子节点。在后序遍历中,首先访问左子节点,然后访问右子节点,最后访问根节点。

下面是前序遍历的示例代码:

// 前序遍历二叉树
// @param root: 二叉树的根节点
void preOrder(TreeNode* root) {
    if(root == nullptr) {
        return;
    }
    cout << root->val << " ";
    preOrder(root->left);
    preOrder(root->right);
}

在上述代码中,先输出当前节点的值,然后递归遍历左子树和右子树。

3. 删除节点

要删除二叉树中的一个节点,首先需要找到该节点。如果该节点没有子节点,则可以直接删除。如果该节点有一个子节点,则需要将该节点的父节点指向子节点。如果该节点有两个子节点,则需要将该节点的右子树中的最小节点拿来替换该节点。

下面是删除二叉树节点的示例代码:

// 删除节点
// @param root: 二叉树的根节点
// @param data: 要删除的节点的值
// @return: 新的根节点
TreeNode* remove(TreeNode* root, int data) {
    if(root == nullptr) {
        return nullptr;   
    }
    if(data < root->val) {
        root->left = remove(root->left, data);
    }
    else if(data > root->val) {
        root->right = remove(root->right, data);
    }
    else {
        // 找到要删除的节点
        if(root->left == nullptr) {
            TreeNode* rightNode = root->right;
            delete root;
            return rightNode;
        }
        if(root->right == nullptr) {
            // 可以省略rightNode,直接return root->left
            TreeNode* leftNode = root->left;
            delete root;
            return leftNode;
        }
        // 用右子树中的最小值节点,替换要删除的节点
        TreeNode* minRightNode = getMinNode(root->right);
        root->val = minRightNode->val;
        root->right = remove(root->right, minRightNode->val);
    }
    return root;
}

// 获取根节点为node的树的最小节点
// @param node: 节点
TreeNode* getMinNode(TreeNode* node) {
    while(node->left != nullptr) {
        node = node->left;
    }
    return node;
}

在上述代码中,getMinNode函数用于获取从指定节点开始的子树中的最小值节点。在remove函数中,首先通过recursion找到要删除的节点。然后,如果该节点为叶子节点或只有一个子节点,直接删除该节点并返回另一个子节点。如果该节点有两个子节点,找到其右子树中的最小值节点,然后用该节点替换要删除的节点,再递归删除该节点。

三、示例说明

下面通过两个示例,展示了如何在C++中实现二叉树的基本操作。

1. 在二叉搜索树中查找元素

// 判断二叉搜索树中是否包含指定元素
// @param root: 二叉树的根节点
// @param data: 指定元素的值
// @return: 是否包含指定元素
bool contains(TreeNode* root, int data) {
    if (root == nullptr) {
        return false;
    }
    if (root->val == data) {
        return true;
    }
    else if (data < root->val) {
        return contains(root->left, data);
    }
    else {
        return contains(root->right, data);
    }
}

int main() {
    TreeNode* root = nullptr;
    insert(root, 5);
    insert(root, 2);
    insert(root, 7);
    insert(root, 1);
    insert(root, 3);

    cout << contains(root, 3) << endl; // true
    cout << contains(root, 8) << endl; // false
}

在上述代码中,首先创建一个二叉搜索树,然后使用contains函数判断该树中是否包含3和8两个元素,结果分别为truefalse

2. 删除二叉搜索树中的一个节点

int main() {
    TreeNode* root = nullptr;
    insert(root, 5);
    insert(root, 2);
    insert(root, 7);
    insert(root, 1);
    insert(root, 3);

    cout << "Before remove: ";
    preOrder(root); // 5 2 1 3 7
    cout << endl;

    root = remove(root, 2);

    cout << "After remove: ";
    preOrder(root); // 5 3 1 7
    cout << endl;
}

在上述代码中,首先创建一个二叉搜索树,并在其中插入了5、2、7、1和3这些节点。然后,使用preOrder函数遍历该树。接下来,删除值为2的节点,并使用preOrder函数遍历树。

在删除节点后,遍历的结果变成了5、3、1和7。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++实现二叉树基本操作详解 - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • 详解C语言之预处理(下)

    下面是“详解C语言之预处理(下)”的完整攻略。 理解C语言中的宏定义 在C语言中,宏定义是一种预处理指令,可以在编译代码前将它们替换为指定的代码片段。这个过程称为宏展开。宏定义的语法格式如下: #define 宏名 宏体 其中,宏名是由字母、数字和下划线组成的标识符,不能以数字开头,而宏体是要替换的代码片段。宏定义还可以带有参数,这种宏定义称为带参数的宏定义…

    C 2023年5月22日
    00
  • 深入了解C++11中promise和future的使用

    深入了解C++11中promise和future 什么是promise和future 在C++11标准中,promise和future是一对用于线程间通信的重要工具。其中,promise负责提供使用者一个方式去异步生成一个值;future则提供了一种方式去访问这个值,或者等待这个值的生成。 可以将promise看作是一个值得承诺,而future则是对这个承诺…

    C 2023年5月22日
    00
  • C++实现AVL树的完整代码

    实现AVL树的完整代码需要遵循以下步骤: 第一步:头文件声明 在代码文件的开头,我们需要声明头文件,以引入所需的库和类。在实现AVL树的完整代码中,我们需要添加以下头文件: #include <iostream> #include <algorithm> 这里用到了C++标准库中的iostream库,用于输入输出操作,以及algori…

    C 2023年5月24日
    00
  • C++ 关键字 inline详细介绍

    当编译器遇到 inline 关键字时,它会像宏一样展开代码。然而,inline 关键字与宏不同,因为编译器将方法调用直接替换成方法的内联代码。此附加信息提示编译器尝试内联代码,但它仍然可以在不允许内联的情况下编译成标准代码。 含义 inline 可以是优化程序效率的一种方式。在调用方法时,程序通常将返回地址、参数等转换为栈中的堆栈桢,再将数据复制到堆栈中。这…

    C 2023年5月30日
    00
  • 使用VScode搭建ROS开发环境的教程详解

    使用VScode搭建ROS开发环境的教程详解 为了在 VScode 中开发 ROS 项目,我们需要以下常用插件: C/C++ 扩展插件 ROS 扩展插件 ROS msg 扩展插件 下面是一个详细的步骤列表,介绍如何准备环境、配置 VScode 以及开发在 ROS 中。 环境准备 为了完成本教程,你需要:1. 一台安装有 Ubuntu 的电脑。2. 你需要在电…

    C 2023年5月23日
    00
  • Java8 ArrayList之forEach的使用

    下面我将为你详细讲解“Java8 ArrayList之forEach的使用”的完整攻略。 1. Java8 ArrayList的使用 在Java中,ArrayList是一种常见的集合类型,它继承自List接口,可以存储多个元素,并且支持动态数组的特性,可以自动扩容。下面是ArrayList的定义: public class ArrayList<E&gt…

    C 2023年5月23日
    00
  • Golang实现解析JSON的三种方法总结

    当我们需要解析JSON格式数据时,Golang提供了三种方法:- 使用encoding/json包- 使用第三方库github.com/tidwall/gjson- 使用第三方库github.com/json-iterator/go 1. encoding/json包解析JSON数据 在Golang中,我们可以使用标准库中的encoding/json包来解析…

    C 2023年5月23日
    00
  • Java使用线程池实现socket编程的方法详解

    Java使用线程池实现socket编程的方法详解 简介 Java中的线程池是用来管理和复用线程的工具。线程池可以减少线程的创建和销毁,节省了系统资源。在socket编程中,线程池可以避免创建大量的线程,优化程序性能。 线程池的实现 线程池的创建可以使用Java中的Executor或ExecutorService接口。这两个类都是Executor框架的一部分,…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部