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