二叉树遍历(C++版本)

二叉树遍历 (Traversal) 是按照某种特定的规则,依次对二叉 树中的结点进行相应的操作,并且每个结点只操作一次 。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

前·中·后序遍历:

按照规则,二叉树的遍历有: 前序 / 中序 / 后序的递归结构遍历
  1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
  2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
  3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
由于被访问的结点必是某子树的根, 所以 N(Node )、 L(Left subtree )和 R(Right subtree )又可解释为 根、根的左子树和根的右子树 NLR LNR LRN 分别又称为先根遍历、中根遍历和后根遍历。
相应地,前序、中序和后序遍历都属于 深度优先遍历(depth-first traversal),也称 深度优先搜索(depth-first search, DFS),它体现了一种“先走到尽头,再回溯继续”的遍历方式。
深度优先遍历就像是绕着整棵二叉树的外围“走”一圈,在每个节点都会遇到三个位置,分别对应前序遍历、中序遍历和后序遍历。

为了更好理解递归,下图展示了前序遍历二叉树的递归过程,其可分为“递”和“归”两个逆向的部分:

  1. “递”表示开启新方法,程序在此过程中访问下一个节点。
  2. “归”表示函数返回,代表当前节点已经访问完毕。

以下使用C语言,利用递归实现二叉树的前·中·后序遍历:
首先:定义一个二叉树节点的结构体:
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
// 二叉树前序遍历
void PreOrder(TreeNode* root);
// 二叉树中序遍历
void InOrder(TreeNode* root);
// 二叉树后序遍历
void PostOrder(TreeNode* root);

前序遍历:

NLR:

实现代码:
void preOrder(TreeNode* node) {
    if (node == nullptr) return;
    std::cout << node->val << " "; // 访问当前节点
    preOrder(node->left);          // 遍历左子树
    preOrder(node->right);         // 遍历右子树
}

按照这种递归方式,我们可以很快理解中序和后序遍历

中序遍历:

LNR:

实现代码:
void inOrder(TreeNode* node) {
    if (node == nullptr) return;
    inOrder(node->left);           // 遍历左子树
    std::cout << node->val << " "; // 访问当前节点
    inOrder(node->right);          // 遍历右子树
}

后序遍历:

LRN:

实现代码:
void postOrder(TreeNode* node) {
    if (node == nullptr) return;
    postOrder(node->left);         // 遍历左子树
    postOrder(node->right);        // 遍历右子树
    std::cout << node->val << " "; // 访问当前节点
}

复杂度:

对于二叉树的前·中·后序遍历:

  • 时间复杂度为 O(n) :所有节点被访问一次,使用 O(n) 时间。
  • 空间复杂度为 O(n) :在最差情况下,即树退化为链表时,递归深度达到 n ,系统占用 O(n) 栈帧空间。

层序遍历:

层序遍历(level-order traversal)从顶部到底部逐层遍历二叉树,并在每一层按照从左到右的顺序访问节点。

层序遍历本质上属于广度优先遍历(breadth-first traversal),也称广度优先搜索(breadth-first search, BFS),它体现了一种“一圈一圈向外扩展”的逐层遍历方式。

广度优先遍历通常借助“队列”来实现。队列遵循“先进先出”的规则,而广度优先遍历则遵循“逐层推进”的规则,两者背后的思想是一致的。 

层序遍历的步骤:

  1. 初始化:使用一个队列来存储待访问的节点。从根节点开始,将其添加到队列中。

  2. 循环遍历:当队列不为空时,执行以下操作:

    • 从队列中取出一个节点(这通常是当前层的第一个节点)。
    • 对该节点进行访问操作,例如打印节点的值。
    • 将该节点的所有未访问过的子节点添加到队列的末尾。
  3. 继续遍历:重复步骤2,直到队列为空,这意味着所有节点已经被访问过。

层序遍历的特点:

  • 层序遍历按照从上到下、从左到右的顺序访问节点。
  • 它使用队列作为辅助数据结构来实现。
  • 层序遍历可以用于实现树的复制、最大深度计算、节点间最短路径查找等。

 实现代码:

#include <iostream>
#include <queue>

using namespace std;

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

void levelOrderTraversal(TreeNode* root) {
    if (!root) return;

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

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

        if (current->left) q.push(current->left); // 添加左子节点
        if (current->right) q.push(current->right); // 添加右子节点
    }
    cout << endl; // 结束当前层的输出
}

注意事项:

  • 在实际应用中,确保在访问完队列中的所有节点后,将它们从队列中移除(q.pop())。
  • 访问节点的操作可以根据具体需求进行修改,例如,可以是打印节点值、计算节点数量等。
  • 确保在添加子节点到队列之前检查它们是否存在,避免添加nullptr到队列。
  • 在使用完动态分配的内存后,记得释放以避免内存泄漏。

层序遍历是一种非常实用的树遍历方法,特别是在需要逐层处理树结构时。


复杂度:

  • 时间复杂度为 O(n) :所有节点被访问一次,使用 O(n) 时间,其中 n 为节点数量。
  • 空间复杂度为 O(n) :在最差情况下,即满二叉树时,遍历到最底层之前,队列中最多同时存在 (n+1)/2 个节点,占用 O(n) 空间。

测试结果:

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);

	std::cout << "Pre-order traversal: ";
	preOrder(root);
	std::cout << std::endl;

	std::cout << "In-order traversal: ";
	inOrder(root);
	std::cout << std::endl;

	std::cout << "Post-order traversal: ";
	postOrder(root);
	std::cout << std::endl;

	std::cout << "Level-order traversal: ";
	levelOrderTraversal(root);
	std::cout << std::endl;

	return 0;
}
Pre-order traversal: 1 2 4 5 3
In-order traversal: 4 2 5 1 3
Post-order traversal: 4 5 2 3 1
Level-order traversal: 1 2 3 4 5

D:\2024C语言\data-structure\二叉树遍历\x64\Debug\二叉树遍历.exe (进程 11512)已退出,代码为 0 (0x0)。
按任意键关闭此窗口. . .
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值