C++编程实例:日期时间与二叉树操作

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:C++编程中,学习日期时间操作与二叉树的存储和打印对于掌握基础知识至关重要。本实例项目涉及使用结构体和类来处理日期时间,涵盖时间转换与格式化。同时,深入探讨二叉树的概念,包括多种类型和遍历方法。通过实际编码练习,学生将学会如何构建、插入、删除和查找二叉树中的节点,以及如何打印二叉树结构,从而增强编程能力。 c++上机实例

1. C++日期时间操作

在编程中,日期和时间的操作是一个非常常见的需求。C++提供了 <chrono> <ctime> 等标准库来处理日期和时间。本章旨在向读者介绍如何在C++中进行日期和时间的基本操作,并逐步深入到高级主题。

1.1 C++中的时间表示

在C++中,时间可以用 std::time_t 类型表示,它是从1970年1月1日(UTC)开始的秒数。 <ctime> 库中的 time() 函数可以返回当前时间的 std::time_t 表示。

#include <iostream>
#include <ctime>

int main() {
    std::time_t currentTime;
    currentTime = time(nullptr); // 获取当前时间
    // 输出当前时间
    std::cout << "当前时间: " << std::ctime(&currentTime);
    return 0;
}

1.2 使用 <chrono> 库进行时间间隔操作

<chrono> 库提供了更现代的时间处理方法。它基于时间点( std::chrono::time_point )和时间间隔( std::chrono::duration )的概念。

#include <iostream>
#include <chrono>

int main() {
    // 创建一个时间点表示当前时间
    auto start = std::chrono::system_clock::now();
    // 模拟一个耗时操作
    std::this_thread::sleep_for(std::chrono::seconds(1));
    // 创建另一个时间点表示操作之后的时间
    auto end = std::chrono::system_clock::now();
    // 计算两个时间点之间的差异
    auto duration = end - start;
    // 输出时间间隔的总秒数
    std::cout << "操作耗时: " << std::chrono::duration_cast<std::chrono::seconds>(duration).count() << "秒" << std::endl;
    return 0;
}

通过上述示例,我们可以看到如何使用C++中的标准库来处理日期和时间。下一章将介绍结构体和类的基本概念与使用实例,这些是面向对象编程的基础。

2. 结构体与类的使用实例

2.1 结构体的基本概念与应用

2.1.1 结构体的定义和初始化

在 C++ 中,结构体是一种用户定义的类型,允许将不同类型的数据项组合成一个单一的复合类型。结构体对于数据封装和管理是不可或缺的。

struct Person {
    std::string name;
    int age;
    char gender;
};

int main() {
    Person p1;  // 默认初始化,成员变量为各自的默认值
    Person p2 = {"John Doe", 30, 'M'}; // 成员初始化列表进行初始化

    return 0;
}

在这个例子中, Person 结构体包含三个数据成员:一个 std::string 类型的 name ,一个 int 类型的 age ,和一个 char 类型的 gender 。在 main 函数中, p1 是使用默认初始化创建的,其中 age 成员将默认初始化为0(基本类型), name gender 成员将使用默认构造函数进行初始化(对于 std::string ,这是一个空字符串)。 p2 则使用了初始化列表进行了显式初始化。

2.1.2 结构体与函数的交互

结构体可以以参数的形式传递给函数,并且函数可以返回结构体类型。这样做可以创建更加模块化和易于理解的代码。

// 给Person结构体添加一个打印信息的成员函数
void printPersonInfo(const Person& p) {
    std::cout << "Name: " << p.name << ", Age: " << p.age << ", Gender: " << p.gender << std::endl;
}

int main() {
    Person p = {"Jane Doe", 25, 'F'};
    printPersonInfo(p); // 调用函数打印Person信息

    return 0;
}

以上代码展示了如何通过函数参数传递结构体,并在函数内部操作结构体成员。

2.2 类的封装与对象的创建

2.2.1 类的定义和成员函数

类在 C++ 中是封装数据和操作这些数据的函数的蓝图。它提供了面向对象编程的基础。类的成员可以是数据成员、函数成员、或者嵌套的类。

class BankAccount {
private:
    double balance; // 私有成员变量,只能被类成员函数访问
public:
    BankAccount(double initialBalance) {
        if (initialBalance > 0)
            balance = initialBalance;
    }

    void deposit(double amount) {
        if (amount > 0)
            balance += amount;
    }

    double getBalance() const {
        return balance;
    }
};

在这个类的定义中, balance 是一个私有成员变量,只能被 BankAccount 类中的成员函数访问。 deposit getBalance 是公有成员函数,可以被类的外部调用。

2.2.2 对象的创建和使用

一旦定义了类,就可以根据这个蓝图创建对象。对象是类实例化的具体表现。

int main() {
    BankAccount account(100.0); // 创建BankAccount对象,初始余额为100.0
    account.deposit(50.0);       // 向账户存入50.0
    std::cout << "Balance: " << account.getBalance() << std::endl; // 打印当前余额

    return 0;
}

在这个例子中,通过构造函数创建了一个 BankAccount 对象 account ,并且调用了对象的成员函数。

2.3 类的继承和多态

2.3.1 继承的概念与实现

继承是面向对象编程中的一项重要特性,它允许创建一个新类来复用、扩展另一个类的功能。

class SavingsAccount : public BankAccount {
private:
    double interestRate;
public:
    SavingsAccount(double initialBalance, double rate)
        : BankAccount(initialBalance), interestRate(rate) {}

    void applyInterest() {
        double interest = getBalance() * interestRate / 100;
        deposit(interest);
    }
};

SavingsAccount 类继承了 BankAccount 类,并添加了一个新的私有成员 interestRate 和一个新的公有成员函数 applyInterest

2.3.2 多态的实现方法与实例

多态允许以统一的接口调用不同类型的对象,从而为不同类的对象提供一种统一的调用方式。多态的实现依赖于虚函数。

class CheckingAccount : public BankAccount {
public:
    CheckingAccount(double initialBalance)
        : BankAccount(initialBalance) {}

    void applyServiceCharge() {
        if (getBalance() > 0)
            deposit(-1.0); // 扣除1.0的服务费
    }
};

// 声明一个基类指针,可以指向派生类对象
BankAccount* accountPtr;

// 根据实际情况,动态分配对象,并指向不同类型的派生类
if (someCondition) {
    accountPtr = new SavingsAccount(200.0, 1.5);
} else {
    accountPtr = new CheckingAccount(100.0);
}

// 通过基类指针调用虚函数,实现多态
accountPtr->deposit(100.0);
accountPtr->applyServiceCharge(); // 或者调用 applyInterest()

// 清理动态分配的资源
delete accountPtr;

在该示例中, SavingsAccount CheckingAccount 都继承自 BankAccount 。通过基类指针 accountPtr ,可以指向任何派生类的对象,并调用在基类中声明为 virtual 的成员函数,实现了多态。

以上内容展示了结构体和类在 C++ 中的使用,包括定义、初始化、与函数的交互以及继承和多态的实现。通过具体的代码示例,我们可以看到面向对象编程中的封装、继承和多态性如何在 C++ 中得以体现。

3. 二叉树概念及其类型

3.1 二叉树的定义和特性

3.1.1 二叉树的基本概念

二叉树是一种重要的数据结构,在计算机科学中有广泛的应用。它是一种每个节点最多有两个子树的树结构,通常子树被称作“左子树”和“右子树”。在二叉树中,每个节点都有一个唯一的根,每个节点的子节点数目被限制为最多两个,这是区分二叉树与其他复杂树结构的关键特征。二叉树在概念上可以是有限的,也可以是无限的,但通常我们在讨论算法和数据结构时指的是有限的二叉树。

3.1.2 二叉树的性质和应用

二叉树的性质是理解其应用和操作的基础。性质包括: - 每个节点最多有两个子节点。 - 对于任意节点,若其左子树非空,则左子树上所有节点的值均小于该节点的值。 - 对于任意节点,若其右子树非空,则右子树上所有节点的值均大于该节点的值。 - 左子树和右子树也分别为二叉树。

二叉树的应用包括但不限于: - 搜索树 :如二叉搜索树(BST),能够高效地进行查找、插入和删除操作。 - :一种特殊的完全二叉树,常用于实现优先队列、堆排序等。 - 哈夫曼树 :用于数据压缩中的哈夫曼编码。

这些应用往往依赖于二叉树的特定结构或操作算法,对二叉树的深入理解对于开发高效算法至关重要。

3.2 二叉树的分类与特点

3.2.1 完全二叉树和满二叉树

完全二叉树是一种特殊的二叉树,除了最后一层外,每一层都是满的,并且最后一层的所有节点都尽可能地集中在左侧。满二叉树指的是所有层都是满的,没有空缺。

  • 完全二叉树 :如果一个二叉树的每一层都是完全填满,但最后一层的节点可以不完全填满,并且最后一层的节点都集中在左边,这样的二叉树称为完全二叉树。
  • 满二叉树 :如果一个二叉树的每一层都是满的,也就是说除了叶子节点,每个节点都有两个子节点,这样的二叉树称为满二叉树。

它们的主要区别在于满二叉树没有空缺节点,而完全二叉树最后一层节点可能不完全。完全二叉树在数组实现上具有优势,因为可以利用索引快速访问节点。

3.2.2 平衡二叉树和AVL树

平衡二叉树(Balanced Binary Tree)是一类能够保证在进行插入和删除操作后依然保持较高效率的二叉树。最著名的平衡二叉树是AVL树。

  • AVL树 :是一种自平衡的二叉搜索树,它在任何节点上两个子树的高度最多相差1。当插入或删除节点后,AVL树会通过旋转操作来保持平衡。AVL树确保了所有基本操作(查找、插入、删除)的时间复杂度维持在O(log n)。

AVL树之所以重要,是因为它在动态数据集合的管理中提供了高效率。然而,维护平衡需要额外的操作,这在某些特定场景下可能会成为性能的瓶颈。

代码示例与逻辑分析

下面的代码示例定义了一个简单的二叉树节点,并实现了一个二叉树的创建。

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

TreeNode* createTree() {
    TreeNode *root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);
    return root;
}

在这个例子中: - struct TreeNode 定义了二叉树节点的数据结构,包含节点的值 val 和指向左右子节点的指针 left right 。 - createTree 函数创建了一个简单的二叉树,并返回根节点的指针。这里构造了一个简单的结构,其中根节点 1 的左子节点为 2 ,右子节点为 3 2 的左子节点为 4 ,右子节点为 5

通过创建这个简单的树结构,我们可以开始探讨更多二叉树的操作和应用。在下一节中,我们将深入探讨二叉树的不同遍历方法及其算法实现。

4. 二叉树的遍历方法

4.1 二叉树遍历的理论基础

4.1.1 遍历的概念及分类

二叉树遍历是二叉树操作中的核心概念,它是指按照某种规则访问树中每个节点且每个节点被访问一次的过程。二叉树遍历的目的是为了实现对树结构的线性化,即将树结构转化为线性结构进行处理。

遍历通常分为四种基本类型:

  • 前序遍历(Pre-order Traversal)
  • 中序遍历(In-order Traversal)
  • 后序遍历(Post-order Traversal)
  • 层序遍历(Level-order Traversal)

前序遍历是指首先访问根节点,然后遍历左子树,最后遍历右子树。中序遍历是指先遍历左子树,然后访问根节点,最后遍历右子树。后序遍历是指先遍历左子树,然后遍历右子树,最后访问根节点。层序遍历则是按照树的层次从上到下、从左到右的顺序访问每个节点。

4.1.2 前序、中序和后序遍历的原理

三种深度优先遍历方法(前序、中序和后序)的原理可以通过递归方法来理解:

  • 前序遍历 :这个过程可以递归定义为,首先处理根节点(通常是打印节点值),然后递归地前序遍历左子树,接着递归地前序遍历右子树。

  • 中序遍历 :中序遍历先处理左子树,然后是根节点,最后是右子树。递归的中序遍历保证了访问的顺序始终是左-根-右。

  • 后序遍历 :后序遍历按照先左子树、再右子树、最后根节点的顺序进行处理。这种方法特别适合于需要在子节点处理完毕后才进行某些操作的场景,比如释放节点内存。

以下是这三种遍历方法的简单伪代码表示:

前序遍历(节点 n):
    if n 不为空:
        访问 n
        前序遍历(n 的左子节点)
        前序遍历(n 的右子节点)

中序遍历(节点 n):
    if n 不为空:
        中序遍历(n 的左子节点)
        访问 n
        中序遍历(n 的右子节点)

后序遍历(节点 n):
    if n 不为空:
        后序遍历(n 的左子节点)
        后序遍历(n 的右子节点)
        访问 n

递归遍历方法的优点是编码简单且直观,但缺点是递归调用会占用较多的栈空间,可能会导致栈溢出错误,特别是在处理非常深的树结构时。非递归遍历方法可以通过使用栈来模拟递归过程,以避免递归带来的潜在问题。

4.2 实现二叉树的遍历算法

4.2.1 递归遍历的实现方式

递归是实现二叉树遍历的最直接和最简单的方式。下面是三种递归遍历方法的C++实现代码:

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

// 前序遍历
void preorderTraversal(TreeNode* root) {
    if (root == nullptr) return;
    // 访问节点
    cout << root->val << " ";
    // 递归遍历左子树
    preorderTraversal(root->left);
    // 递归遍历右子树
    preorderTraversal(root->right);
}

// 中序遍历
void inorderTraversal(TreeNode* root) {
    if (root == nullptr) return;
    // 递归遍历左子树
    inorderTraversal(root->left);
    // 访问节点
    cout << root->val << " ";
    // 递归遍历右子树
    inorderTraversal(root->right);
}

// 后序遍历
void postorderTraversal(TreeNode* root) {
    if (root == nullptr) return;
    // 递归遍历左子树
    postorderTraversal(root->left);
    // 递归遍历右子树
    postorderTraversal(root->right);
    // 访问节点
    cout << root->val << " ";
}

每种遍历方法都定义了一个函数,该函数接受一个指向树的根节点的指针,并递归地遍历该树。遍历过程中的访问动作取决于具体的遍历方法,前序遍历在访问子树之前访问根节点,中序遍历在访问左子树之后、右子树之前访问根节点,后序遍历在访问完所有子树后访问根节点。

4.2.2 非递归遍历的实现方式

为了克服递归遍历可能引起的栈溢出问题,我们可以使用迭代法(非递归)来进行遍历。这通常需要使用栈来模拟递归过程。以下是三种非递归遍历方法的C++实现代码:

void preorderTraversalNonRecursive(TreeNode* root) {
    if (root == nullptr) return;
    stack<TreeNode*> stack;
    stack.push(root);
    while (!stack.empty()) {
        TreeNode* node = stack.top();
        stack.pop();
        // 访问节点
        cout << node->val << " ";
        // 先压入右子节点,后压入左子节点,因为栈是后进先出
        if (node->right) stack.push(node->right);
        if (node->left) stack.push(node->left);
    }
}

void inorderTraversalNonRecursive(TreeNode* root) {
    stack<TreeNode*> stack;
    TreeNode* current = root;
    while (current != nullptr || !stack.empty()) {
        while (current != nullptr) {
            stack.push(current);
            current = current->left;
        }
        current = stack.top();
        stack.pop();
        // 访问节点
        cout << current->val << " ";
        current = current->right;
    }
}

void postorderTraversalNonRecursive(TreeNode* root) {
    if (root == nullptr) return;
    stack<TreeNode*> stack;
    TreeNode* lastVisited = nullptr;
    stack.push(root);
    while (!stack.empty()) {
        TreeNode* topNode = stack.top();
        // 判断是应该访问根节点还是向左或向右移动
        if (lastVisited == nullptr || lastVisited->left == topNode || lastVisited->right == topNode) {
            // 可以向左或向右移动
            if (topNode->left) stack.push(topNode->left);
            else if (topNode->right) stack.push(topNode->right);
        } else if (topNode->left == lastVisited) {
            // 如果向左移动过了,尝试向右移动
            if (topNode->right) stack.push(topNode->right);
        } else {
            // 访问节点
            cout << topNode->val << " ";
            lastVisited = topNode;
        }
        stack.pop();
    }
}

在非递归遍历中,栈的使用是非常关键的。栈的后进先出特性帮助我们记录了节点的访问路径,并且也使得节点能够按照所需的顺序被访问。

例如,在前序遍历中,节点被首先压入栈中,然后在每次迭代中,栈顶节点被访问并从栈中弹出,之后它的右子节点和左子节点先后被压入栈中。这样就保证了左子节点先于右子节点被访问,因为在栈中右子节点会先被处理。

非递归遍历虽然在实现上比递归方式复杂,但它有效地避免了递归可能导致的栈溢出问题,并且在某些情况下也能提供更好的性能。

为了更好地理解遍历过程,以下是展示前序遍历的mermaid流程图,它帮助可视化迭代法中的栈操作:

graph TD;
    A[开始] --> B{栈是否为空}
    B -- 否 --> C[弹出栈顶]
    C --> D[访问节点]
    D --> E{节点是否有右子节点}
    E -- 是 --> F[将右子节点压入栈]
    F --> G{节点是否有左子节点}
    E -- 否 --> G
    G -- 是 --> H[将左子节点压入栈]
    H --> B
    G -- 否 --> B
    B -- 是 --> I[结束]

在实际应用中,选择递归遍历还是非递归遍历取决于具体的应用场景和需求。递归遍历代码更简洁易懂,但需要注意递归深度限制;非递归遍历在处理深度大的树时更为安全,但实现复杂度较高。

5. 二叉树节点的插入、删除和查找操作

5.1 二叉树节点的插入操作

5.1.1 插入操作的原理

在二叉搜索树中,新节点的插入位置是根据节点的值来决定的。插入操作的基本思想是保持二叉搜索树的性质,即对于树中的任意节点N,其左子树中的所有元素的值都小于或等于节点N的值,其右子树中的所有元素的值都大于节点N的值。

在进行插入操作时,首先从根节点开始搜索,比较新节点的值与当前节点的值:

  • 如果新节点的值小于当前节点的值,向左子树递归;
  • 如果新节点的值大于当前节点的值,向右子树递归;
  • 如果找到一个空节点,则将新节点插入到该位置。

5.1.2 插入算法的实现

以下是一个递归方式的二叉树节点插入算法的实现示例。假设我们使用C++来实现这个算法。

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

TreeNode* insertIntoBST(TreeNode* root, int val) {
    if (root == nullptr) return new TreeNode(val);

    if (val < root->val)
        root->left = insertIntoBST(root->left, val);
    else if (val > root->val)
        root->right = insertIntoBST(root->right, val);

    return root;
}

在上述代码中,我们首先检查根节点是否为空。如果为空,我们直接创建一个新的节点并返回。否则,我们根据值的大小递归地向左子树或右子树插入。

逻辑分析:

  • root == nullptr 检查当前子树是否为空,如果是,则直接插入新节点并返回。
  • val < root->val 如果新值小于当前节点的值,递归调用左子树。
  • val > root->val 如果新值大于当前节点的值,递归调用右子树。

此算法的时间复杂度为O(h),其中h为树的高度。在最好的情况下,树是完全平衡的,时间复杂度为O(logn)。在最坏的情况下,树高度为n,时间复杂度为O(n),例如链式结构。

5.2 二叉树节点的删除操作

5.2.1 删除操作的原理

二叉搜索树的删除操作稍微复杂一些,因为它有三种可能的情况:

  • 被删除的节点是叶子节点:直接删除节点并将其父节点的相应指针设置为NULL。
  • 被删除的节点只有一个子节点:删除节点,并用其子节点替换它。
  • 被删除的节点有两个子节点:找到该节点的中序后继(即右子树中的最小节点),或中序前驱(即左子树中的最大节点),用该节点替换要删除的节点,并删除找到的中序后继或前驱节点(它必定只有一个子节点或没有子节点)。

5.2.2 删除算法的实现

以下是C++实现的删除节点算法代码:

TreeNode* deleteNode(TreeNode* root, int key) {
    if (root == nullptr) return root;
    if (key < root->val)
        root->left = deleteNode(root->left, key);
    else if (key > root->val)
        root->right = deleteNode(root->right, key);
    else {
        // Node with only one child or no child
        if (root->left == nullptr) {
            TreeNode* temp = root->right;
            delete root;
            return temp;
        } else if (root->right == nullptr) {
            TreeNode* temp = root->left;
            delete root;
            return temp;
        }
        // Node with two children: Get the inorder successor (smallest in the right subtree)
        TreeNode* temp = minValueNode(root->right);
        // Copy the inorder successor's content to this node
        root->val = temp->val;
        // Delete the inorder successor
        root->right = deleteNode(root->right, temp->val);
    }
    return root;
}

// Helper function to find the node with minimum value (leftmost)
TreeNode* minValueNode(TreeNode* node) {
    TreeNode* current = node;
    while (current && current->left != nullptr)
        current = current->left;
    return current;
}

逻辑分析:

  • 我们首先检查当前节点是否为空。如果是,返回空指针。
  • 如果要删除的值小于当前节点的值,递归地删除左子节点。
  • 如果要删除的值大于当前节点的值,递归地删除右子节点。
  • 如果找到要删除的节点,根据其子节点的数量采取不同的策略:
  • 如果节点有两个子节点,找到它的中序后继节点,替换当前节点的值,并删除中序后继节点。
  • 如果节点只有一个子节点或没有子节点,直接删除该节点,并用其子节点替换。

5.3 二叉树节点的查找操作

5.3.1 查找操作的原理

二叉搜索树的查找操作是基本操作之一,用于快速定位树中的特定节点。查找过程从根节点开始,比较节点值,并在树中向下移动:

  • 如果要查找的值小于当前节点的值,则向左子树查找;
  • 如果要查找的值大于当前节点的值,则向右子树查找;
  • 如果值相等,则找到了节点;
  • 如果到达叶子节点还没有找到,则说明目标值不在树中。

5.3.2 查找算法的实现

以下是C++实现的二叉树查找算法代码:

TreeNode* searchBST(TreeNode* root, int val) {
    if (root == nullptr || root->val == val)
        return root;

    if (val < root->val)
        return searchBST(root->left, val);
    else
        return searchBST(root->right, val);
}

逻辑分析:

  • 我们首先检查当前节点是否为空。如果是,说明我们已经到达树的末尾,没有找到节点,返回空指针。
  • 如果当前节点的值等于要查找的值,说明找到了目标节点,返回当前节点。
  • 如果要查找的值小于当前节点的值,递归地在左子树中查找。
  • 如果要查找的值大于当前节点的值,递归地在右子树中查找。

这个查找算法的时间复杂度是O(h),其中h是树的高度。在平衡的二叉搜索树中,查找操作的时间复杂度为O(logn)。在极端不平衡的情况下,例如链式结构,时间复杂度会退化到O(n)。

6. 二叉树的打印实现

6.1 二叉树打印的策略和方法

6.1.1 打印二叉树的理论基础

在数据结构中,二叉树是一种重要的非线性结构。其打印实现是将树形结构转换为线性形式,以方便查看和进一步处理。通常,二叉树的打印可以采用多种策略,包括前序、中序、后序以及层序遍历。这些遍历方法在上一章节已经讨论过,它们以不同的顺序访问树的节点,从而能够以不同的方式打印二叉树。

6.1.2 层序遍历的打印方法

层序遍历(Level-Order Traversal)是一种按照树的层次从上到下、从左到右的顺序访问树的节点。为了实现层序遍历,我们通常使用队列这一数据结构来辅助。队列的先进先出(FIFO)特性保证我们能够按照层级顺序访问节点。

6.2 打印二叉树的实例演示

6.2.1 代码实现与分析

为了演示层序遍历打印二叉树的过程,我们先定义一个二叉树节点的结构,并实现层序遍历的函数。

#include <iostream>
#include <queue>

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

// 层序遍历打印函数
void printLevelOrder(TreeNode* root) {
    if (root == NULL) return;

    std::queue<TreeNode*> q;
    q.push(root);

    while (!q.empty()) {
        TreeNode* node = q.front();
        q.pop();
        std::cout << node->val << " "; // 访问节点

        // 将非空的左右子节点加入队列
        if (node->left != NULL) q.push(node->left);
        if (node->right != NULL) q.push(node->right);
    }
}

6.2.2 实例运行结果展示

假设我们有如下的二叉树:

    1
   / \
  2   3
 / \   \
4   5   6

使用我们之前定义的 printLevelOrder 函数进行层序遍历,输出结果应该为: 1 2 3 4 5 6 。这正是树按层次从上到下、从左到右的顺序访问节点的结果。

接下来,我们创建一个二叉树实例,并使用层序遍历打印出来:

int main() {
    // 构建二叉树
    TreeNode *root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);
    root->right->right = new TreeNode(6);

    // 层序遍历打印二叉树
    std::cout << "Level Order Traversal: ";
    printLevelOrder(root);
    std::cout << std::endl;

    // 清理分配的内存
    delete root->left->left;
    delete root->left->right;
    delete root->right->right;
    delete root->left;
    delete root->right;
    delete root;

    return 0;
}

执行以上代码,我们将看到如下输出:

Level Order Traversal: 1 2 3 4 5 6

这验证了我们的层序遍历函数能够正确地打印出二叉树的节点。

通过这个章节的讲解,我们不仅了解了打印二叉树的理论基础,还通过实际的代码实现了层序遍历,并展示了运行结果。希望本章节的内容能够帮助你更好地理解和掌握二叉树的打印方法。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:C++编程中,学习日期时间操作与二叉树的存储和打印对于掌握基础知识至关重要。本实例项目涉及使用结构体和类来处理日期时间,涵盖时间转换与格式化。同时,深入探讨二叉树的概念,包括多种类型和遍历方法。通过实际编码练习,学生将学会如何构建、插入、删除和查找二叉树中的节点,以及如何打印二叉树结构,从而增强编程能力。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值