详解二叉树的各种非递归遍历

简介: 详解二叉树的各种非递归遍历

   递归的本质就是压栈、回溯、弹栈,那我们模拟他这个过程。

大概可以写成下面的过程:

  1. 创建一个栈(可以是数组实现的栈或者使用语言提供的栈数据结构),用于暂存遍历过程中的节点。
  2. 初始化当前节点为根节点。
  3. 进入循环,判断条件为当前节点不为空或栈不为空。
  4. 在循环中,执行以下操作:
  • 如果当前节点不为空,将当前节点入栈,并将当前节点指向其左子节点(进入左子树)。
  • 如果当前节点为空,说明已经到达左子树的最底部,从栈中取出一个节点,访问该节点,并将当前节点指向其右子节点(进入右子树)。
  • 不断重复上述过程,直到当前节点为空且栈为空,完成遍历。

前序:

  • 创建一个栈,将根节点入栈。
  • 进入循环,判断条件为栈不为空。
  • 在循环中,取出栈顶节点,访问该节点,并依次将其右子节点和左子节点入栈。
// 非递归前序遍历函数
void preOrderTraversal(TreeNode* root) {
    if (root == nullptr)
        return;
 
    stack<TreeNode*> nodeStack; // 创建一个栈用于存储结点
 
    nodeStack.push(root); // 将根结点入栈
 
    while (!nodeStack.empty()) {
        TreeNode* currentNode = nodeStack.top(); // 取出栈顶结点
        nodeStack.pop(); // 弹出栈顶结点
 
        cout << currentNode->val << " "; // 访问当前结点
 
        if (currentNode->right)
            nodeStack.push(currentNode->right); // 先将右子结点入栈,因为前序遍历的顺序是根-左-右
        if (currentNode->left)
            nodeStack.push(currentNode->left); // 再将左子结点入栈
    }
}

中序:

  • 创建一个栈。
  • 从根节点开始,将当前节点和所有左子节点按顺序入栈,直到最左的叶子节点。
  • 进入循环,判断条件为当前节点为空且栈为空。
  • 在循环中,取出栈顶节点,访问该节点,并将当前节点指向其右子节点
vector<int> inorderTraversal(TreeNode* root) {
    vector<int> result; // 存放遍历结果的向量
    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();
        result.push_back(current->val);
 
        // 遍历右子树
        current = current->right;
    }
 
    return result;
}

后序:


  • 创建两个栈。一个栈用于存储节点的顺序,另一个栈用于存储实际的后序遍历结果。
  • 将根节点入栈1。
  • 进入循环,判断条件为栈1不为空。
  • 在循环中,取出栈1的栈顶节点,将该节点入栈2,并依次将其左子节点和右子节点入栈1。
  • 重复上述过程直到栈1为空。此时,栈2中存储的就是后序遍历的结果,可以按顺序访问栈2中的节点。
// 非递归后序遍历函数
void postOrderTraversal(TreeNode* root) {
    if (root == nullptr)
        return;
        
    stack<TreeNode*> nodeStack; // 创建一个栈用于存储结点
    TreeNode* prevNode = nullptr; // 记录上一个访问过的结点
 
    while (root || !nodeStack.empty()) {
        if (root) {
            nodeStack.push(root); // 将当前结点入栈
            root = root->left; // 访问左子结点
        } else {
            TreeNode* topNode = nodeStack.top();
 
            if (topNode->right && topNode->right != prevNode) {
                root = topNode->right; // 若右子结点存在且未被访问过,则访问右子结点
            } else {
                cout << topNode->val << " "; // 访问当前结点
                nodeStack.pop(); // 弹出栈顶结点
                prevNode = topNode; // 更新上一个结点为当前结点
            }
        }
    }
}
相关文章
|
7月前
|
存储 算法 Java
二叉树层序遍历
二叉树层序遍历
59 0
|
7月前
|
算法
【二叉树】层序遍历
【二叉树】层序遍历
76 0
|
存储 C++
非递归遍历二叉树
非递归遍历二叉树
52 0
08_N叉树的层序遍历
08_N叉树的层序遍历
05_二叉树的层次遍历II
05_二叉树的层次遍历II
|
7月前
|
算法
二叉树的递归遍历和非递归遍历
二叉树的递归遍历和非递归遍历
33 0
|
存储
线索化二叉树
线索化二叉树
59 0
LeetCode——二叉树的非递归遍历
LeetCode——二叉树的非递归遍历
|
算法
二叉树的层次遍历
层次遍历就是即逐层地,从左到右访问所有节点
二叉树的层次遍历