美文网首页
二叉树的各类遍历方法

二叉树的各类遍历方法

作者: 顽强的猫尾草 | 来源:发表于2018-05-15 21:45 被阅读17次

二叉树的遍历主要有四种:前序、中序、后序、层序。

遍历的实现方式主要是:递归和非递归。
递归遍历的实现非常容易,非递归的实现需要用到栈,难度要高一点。

节点的定义

class TreeNode {
public:
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
}

前序遍历

先访问根节点,再访问左子树,最后访问右子树。

递归版本

vector<int> preorderTraversal(TreeNode* root) {
    vector<int> ret;
    preOrder(root, ret);
    return ret;
}
void preorder(TreeNode* p, vector<int> &ret) {
    if(p==NULL) return;
    ret.push_back(p->val);      // 存储根节点
    preorder(p->left, ret);     // 访问左子树
    preorder(p->right, ret);    // 访问右子树
}

非递归版本

需要利用辅助栈来实现:

  1. 首先把根节点压入栈中;
  2. 此时栈顶元素即为当前根节点,弹出并访问即可;
  3. 把当前根节点的右子树和左子树分别入栈,考虑到栈是先进后出,所以必须右子树先入栈,左子树后入栈;
  4. 重复 2~3 步骤,直到栈为空。
vector<int> preorderTraversal(TreeNode* root) {
    vector<int> ret;
    if (root==NULL) return ret;
    stack<TreeNode*> st;
    st.push(root);
    while(!st.empty()) {
        TreeNode* tp = st.top();    //取出栈顶元素
        st.pop();
        ret.push_back(tp->val);     //先访问根节点
        if(tp->right!=NULL) st.push(tp->right);    //由于栈时先进后出,考虑到访问顺序,先将右子树压栈
        if(tp->left!=NULL) st.push(tp->left);      //将左子树压栈
    }
    return ret;
}

中序遍历

先访问左子树,再访问根节点,最后访问右子树。

递归版本

vector<int> inorderTraversal(TreeNode* root) {
    vector<int> ret;
    inorder(root, ret);
    return ret;
}
void inorder(TreeNode* p, vector<int>& ret) {
    if(p==NULL) return;
    inorder(p->left, ret);     // 访问左子树
    ret.push_back(p->val);     // 访问根节点
    inorder(p->right, ret);    // 访问右子树
}

非递归版本

中序遍历的非递归版本比前序稍微复杂一点,除了用到辅助栈之外,还需要一个指针 p 指向下一个待访问的节点:

  • 如果 p 非空,则将 p 入栈,p 指向 p 的左子树;
  • 如果 p 为空,说明此时左子树已经访问到尽头了,弹出当前栈顶元素,进行访问,并把 p 设置成 p 的右子树的左子树,即下一个待访问的节点。
vector<int> inorderTraversal(TreeNode* root) {
    vector<int> ret;
    TreeNode* p = root;
    stack<TreeNode*> st;
    while(!st.empty() || p!=NULL){
        if(p) {    // p 非空,代表还有左子树,继续
            st.push(p);
            p=p->left;
        }
        else {    // 如果为空,代表左子树已经走到尽头了
            p = st.top();
            st.pop();
            ret.push_back(p->val);     // 访问栈顶元素
            if(p->right) {
                st.push(p->right);     // 如果存在右子树,将右子树入栈
                p = p->right->left;    // p 始终为下一个待访问的节点
            }
            else p=NULL;
        }
    }
    return ret;
}

后序遍历

先访问左子树,再访问右子树,最后访问根节点。

递归版本

vector<int> postorderTraversal(TreeNode* root) {
    vector<int> ret;
    postorder(root, ret);
    return ret;
}
void postorder(TreeNode* p, vector<int>& ret) {
    if(p==NULL) return;
    postorder(p->left, ret);
    postorder(p->right, ret);
    ret.push_back(p->val);
}

非递归版本

采用一个辅助栈和两个指针 p 和 r,p 代表下一个需要访问的节点,r 代表上一次需要访问的节点:

  1. 如果 p 非空,则将 p 入栈,p 指向 p 的左子树;
  2. 如果 p 为空,代表左子树到了尽头,此时判断栈顶元素:
    2.1 如果栈顶元素存在右子树且没有被访问过(等于 r 代表被访问过),则右子树入栈,p 指向右子树的左子树;
    2.2 如果栈顶元素不存在或者已经被访问过,则弹出栈顶元素,访问,然后 p 置为 NULL,r 记录上一次访问的节点 p。
vector<int> postorderTraversal(TreeNode* root) {
    vector<int> ret;
    TreeNode* p = root;
    stack<TreeNode*> st;
    TreeNode* r = NULL;
    while(p || !st.empty()) {
        if(p) {
            st.push(p);
            p = p->left;
        }
        else {
            p = st.top();
            if(p->right && p->right!=r) {
                p = p->right;
                st.push(p);
                p = p->left;
            }
            else {
                p = st.top();
                st.pop();
                ret.push_back(p->val);
                r = p;
                p = NULL;
            }
        }
    }
    return ret;
}

层序遍历

每一层从左到右访问每一个节点。
层序遍历需要借助队列 queue 来完成,因为要满足先进先去的访问顺序。

vector<vector<int>> levelOrder(TreeNode* root) {
    vector<vector<int>> ret;
    if(root==NULL) return ret;
    queue<TreeNode*> q;
    q.push(root);
    while(!q.empty()) {
        vector<int> temp;
        queue<TreeNode*> tmpQue;    // 存储下一层需要访问的节点
        while(!q.empty()) {    // 从左到右依次访问本层
            TreeNode* tempNode = q.front();
            q.pop();
            temp.push_back(tempNode->val);
            if(tempNode->left!=NULL) tmpQue.push(tempNode->left);      // 左子树压入队列
            if(tempNode->right!=NULL) tmpQue.push(tempNode->right);    // 右子树压入队列
        }
        ret.push_back(temp);
        q = tmpQue;    // 访问下一层
    }
    return ret;
}

相关文章

  • 二叉树的各种遍历方法

    二叉树的常用遍历方法 二叉树常用的遍历方法包括: 前序遍历 中序遍历 后序遍历 层次遍历 而前三种遍历的具体实现上...

  • 算法精选题总结之二叉树类

    1.二叉树的中序遍历中序遍历的迭代方法中序遍历的递归方法2.二叉树前序遍历3.二叉树后续遍历4.二叉树的最近公共祖...

  • 不用递归或栈遍历二叉树

    上回说到二叉树的各类遍历方法,其中前中后序遍历都是用栈或者递归(实际上还是栈)来实现的。 如果只给你 O(1) 空...

  • 【算法打卡60天】Day18二叉树基础(下)

    Day19学习内容:二叉树各种遍历方法,以及各自的特点。遍历主要分为如下三种方式:1.二叉树的遍历方法一:前序遍历...

  • 二叉树的各类遍历方法

    二叉树的遍历主要有四种:前序、中序、后序、层序。 遍历的实现方式主要是:递归和非递归。递归遍历的实现非常容易,非递...

  • 二叉树的一些基本知识总结

    学了学二叉树,这里说说怎样遍历二叉树.四种方式:前序遍历,中序遍历,后序遍历,层次遍历. 主要说说递归的遍历方法前...

  • 二叉树的基本操作

    一、基本内容 二叉树的创建(先顺遍历的方法) 二叉树的先序遍历 二叉树的中序遍历 二叉树的后序遍历 哈夫曼树的创建...

  • 深度优先遍历中的先序遍历(二叉树)

    第一,初始化二叉树。 第二,二叉树的先序遍历。(运用递归的方法) 第三,调用方法。 这就是深度优先遍历中的先序遍历...

  • 二叉树(2)- 遍历二叉树

    有任何问题,欢迎交流!微博@HelloWorld-_-接着创建二叉树文章说一说遍历二叉树的几种方法 二叉树遍历方法...

  • 二叉树遍历(先序、中序、后序)

    二叉树有多种遍历方法,有层次遍历、深度优先遍历、广度优先遍历等。 本文只涉及二叉树的先序、中序、后序的递归和非递归...

网友评论

      本文标题:二叉树的各类遍历方法

      本文链接:https://www.haomeiwen.com/subject/iviydftx.html