二叉树的4种遍历方式总结

1 前序遍历

前序、中序、后序遍历的递归版本非常简单,不再赘述,这里仅给出非递归版本的实现。首先是前序遍历,顺序为根左右。具体的,先访问根节点,再访问左子树的根节点,再访问左子树的左子树的根节点,依次下去,直到左子树为空。左子树不存在时,接下来就需要访问最近访问的节点的右子树,对此右子树依然按照上述流程遍历。由于要访问右子树,我们就得记录之前一路向左访问下来的节点,且最近被访问的节点应该记录在最前面,这当然会让我们想到结构,至此代码就不难写了:

/* 返回前序遍历的节点值 */
vector<int> preorderTraversal(TreeNode* root) {
    vector<int> ret;
    stack<TreeNode *> s;
    
    for (TreeNode *cur = root; cur || !s.empty(); ) {
    	/* 访问并入栈一系列的左孩子 */
        for (; cur; cur = cur->left) {
            s.push(cur);
            ret.emplace_back(cur->val);
        }
		/* 当不再有左孩子可以入队的时候取出栈顶元素(最后访问的左孩子)的右孩子 */
        cur = s.top()->right;
        s.pop();
    }

    return ret;
}

2 中序遍历

中序遍历的顺序为左根右,其程序实现和前序遍历非常类似,唯一不同是将根节点、左子树的根节点、左子树的左子树的根节点…入队的时候不进行访问,因为根节点是在左子树访问完之后才访问的,因此访问节点的时机在元素出队的时候。程序如下:

/* 返回中序遍历的节点值 */
vector<int> inorderTraversal(TreeNode* root) {
    vector<int> ret;
    stack<TreeNode *> s;
    
    for (TreeNode *cur = root; cur || !s.empty(); ) {
    	/* 不断的将左孩子入栈(不访问) */
        for (; cur; cur = cur->left)
            s.push(cur);
		/* 访问栈顶元素并获取其右孩子 */
        ret.emplace_back(s.top()->val);
        cur = s.top()->right;
        s.pop();
    }

    return ret;
}

3 后序遍历

后序遍历的顺序为左右根。虽然不是很直观,但我们仍然可以套用前序、中序的程序来实现后序遍历。可以这么考虑,前序遍历实现了根左右,实际上左和右谁在前谁在后在实现上很容易调整,也就是说稍加修改前序遍历的程序即可实现根右左,显然这个顺序如果反一下,就得到了后序遍历的顺序。那么,怎么反一下呢?还是借助栈结构!程序如下:

/* 返回后序遍历的节点值 */
vector<int> postorderTraversal(TreeNode* root) {
    vector<int> ret;
    stack<TreeNode *> s1, s2;
    
    for (TreeNode *cur = root; cur || !s1.empty(); ) {
        /* 入栈一系列的"右"孩子 */
        for (; cur; cur = cur->right) {
            s1.push(cur);
            s2.push(cur);   /* 用s2来转储(替代访问操作) */
        }
        /* 当不再有"右"孩子可以入队的时候取出栈顶元素的"左"孩子 */
        cur = s1.top()->left;
        s1.pop();
    }

    /* 按照后续遍历的顺序访问节点 */
    for (; !s2.empty(); s2.pop())
        ret.emplace_back(s2.top()->val);

    return ret;
}

当然,后序遍历还有其它的实现思路,比如为每个节点设置一个flag,当节点首次入栈时标志为0。节点出栈时,如果标记为0,则将其再次压入栈,并将标志置1,同时该节点的右子树、左子树依次入栈。只有当节点的标志为1时,节点才能真正出栈并访问,此时说明该节点的左右子树都访问过了。程序如下:

/* 返回后序遍历的节点值 */
vector<int> postorderTraversal(TreeNode* root) {
    vector<int> ret;
    stack<pair<TreeNode *, bool>> s;

    if (root)
        s.push(make_pair(root, false));
    
    while (!s.empty()) {
        if (s.top().second) {
            ret.emplace_back(s.top().first->val);
            s.pop();
        } else {
        	/* 将标记置为true */
            s.top().second = true;
            TreeNode *left = s.top().first->left;
            TreeNode *right = s.top().first->right;
            /* 当前栈顶是子树的根,然后按照右、左的顺序入栈,则出栈时顺序为左右根 */
            if (right) s.push(make_pair(right, false));
            if (left) s.push(make_pair(left, false));
        }
    }

    return ret;
}

4 层序遍历

层序遍历是典型的BFS(宽度优先搜索),借助一个队列即可实现,代码很简单:

/* 返回每一层的节点值 */
vector<vector<int>> levelOrder(TreeNode* root) {
    vector<vector<int>> ret;
    queue<TreeNode *> q;

    if (root)
        q.push(root);

    while (!q.empty()) {
        vector<int> tmp;
        for (int size = q.size(); size; --size) {
            TreeNode *node = q.front();
            q.pop();
            tmp.emplace_back(node->val);
            if (node->left) q.push(node->left);
            if (node->right) q.push(node->right);
        }
        ret.emplace_back(std::move(tmp));
    }

    return ret;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值