首先二叉树的数据结构表示:
/** * Definition for binary tree * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */
1. 先序遍历(递归版本)
2. 中序遍历(递归版本)
3. 后序遍历(递归版本)
4. 先序遍历(非递归版本)
5. 中序遍历(非递归版本)
6. 后序遍历(非递归版本)
7. 中序遍历(非递归版本,不用栈, Morris遍历)
8. 先序遍历(非递归版本,不用栈, Morris遍历)
9. 层次遍历(递归版本)
10. 层次遍历(非递归版本)
1. 先序遍历(递归版本)
class Solution { public: vector<int> preorderTraversal(TreeNode *root) { vector<int> res; preorder(root, res); return res; } void preorder(TreeNode* root, vector<int>& res) { if (root == NULL) return; if (root != NULL) { res.push_back(root->val); preorder(root->left, res); preorder(root->right, res); } } };
2. 中序遍历(递归版本)
class Solution { public: vector<int> inorderTraversal(TreeNode *root) { vector<int> res; inorder(root, res); return res; } void inorder(TreeNode* root, vector<int>& res) { if (root == NULL) return; if (root != NULL) { inorder(root->left, res); res.push_back(root->val); inorder(root->right, res); } } };
3.后序遍历(递归版本)
class Solution { public: vector<int> postorderTraversal(TreeNode *root) { vector<int> res; postorder(root, res); return res; } void postorder(TreeNode* root, vector<int>& res) { if (root == NULL) return; if (root != NULL) { postorder(root->left, res); postorder(root->right, res); res.push_back(root->val); } } };
4. 先序遍历(非递归版本)
class Solution { public: vector<int> preorderTraversal(TreeNode *root) { vector<int> res; stack<TreeNode*> stk; while (!stk.empty() || root != NULL) { if (root != NULL) { res.push_back(root->val); stk.push(root); root = root->left; } else { TreeNode* p = stk.top(); stk.pop(); root = p->right; } } return res; } };
5. 中序遍历(非递归版本)
class Solution { public: vector<int> inorderTraversal(TreeNode *root) { vector<int> res; stack<TreeNode*> stk; while (!stk.empty() || root != NULL) { if (root != NULL) { stk.push(root); root = root->left; } else { TreeNode* p = stk.top(); stk.pop(); res.push_back(p->val); root = p->right; } } return res; } };
6. 后序遍历(非递归版本)
这里讲解一下,非递归遍历的时候,我们最需要关注的根节点的入栈情况。
6.1 在先序遍历中,因为是 根左右 的顺序,所以每当遍历到根节点时,我们首先就访问它并且将它入栈
6.2 在中序遍历中,因为是 左右根 的顺序,所以每当遍历到某节点时,我们只能将它入栈,并不能立即访问它,必须等它的所有左子树节点都访问完之后才能访问该节点。
什么时候才是左子树节点全部访问完呢?
我们可以注意到: 每次入栈节点之后,接下来操作的都是其左儿子,也就是说 入栈顺序是:当前节点-->左儿子-->左孙子...
因为栈的后入先出的特性,我们可以推出 当从栈中弹出的是 当前节点的时候,那么其左孙子、左儿子..一定已经依次出栈了并且被访问了,这个时候我们就可以访问该当前节点了
6.3 在后序遍历中,因为是 左右根 的顺序,所以每当遍历到某节点时,我们只能将它入栈,并不能立即访问它,
必须等它的所有左子树节点 和 它的所有右子树节点 都访问完之后才能访问该节点。
什么时候才是其左子树节点和右子树节点全部访问完呢?
我们可以注意到: 每次入栈节点之后,接下来操作的都是其左儿子,也就是说 入栈顺序是:当前节点-->左儿子-->左孙子...
因为栈的后入先出的特性,我们可以推出 当从栈中弹出的是 当前节点的时候,那么其左孙子、左儿子..一定已经依次出栈了并且被访问了
但是,这个时候我们还不能访问该当前节点了!!! 因为右子树节点都没有访问...
这个时候,该节点的右子树节点入栈,当该节点第二次出栈(第一次应该要出栈,但是不能出栈)的时候才能访问。
我们换个思路,后序遍历是 左右根,为什么不能像先序或者中序一样简单呢? 最根本的原因就在于 根节点是最后被访问。
我们反转一下,将 左右根 反转为 根右左, 然后将结果反转就可以得到最终结果了,这里我们先转化为 根右左 是因为这个根节点最先访问,非常简单。
怎样进行 根右左 遍历? 这个就简单了,只需要将 先序遍历的左右互换就可以了。
class Solution { public: vector<int> postorderTraversal(TreeNode *root) { vector<int> res; stack<TreeNode*> stk; while (!stk.empty() || root != NULL) { if (root != NULL) { res.push_back(root->val); stk.push(root); root = root->right; } else { TreeNode* p = stk.top(); stk.pop(); root = p->left; } } reverse(res.begin(), res.end()); return res; } };
想了想,这样转化的方法不是很优雅,这样只是AC了这道题,但是没有按照 后序遍历的本质去解答
访问不只是 打印输出,如果要对每个节点干点别的事呢? 这时这个办法就没用了。看来还是得 按照 后序遍历的思路来,只是稍微麻烦些。
class Solution { public: vector<int> postorderTraversal(TreeNode *root) { vector<int> res; if (root == NULL) return res; stack<TreeNode*> stk; TreeNode* cur = root; // cur为正在访问的结点 TreeNode* prev = NULL; // prev为刚刚访问过的结点 do { while (cur != NULL) { // 一直往左下走 stk.push(cur); cur = cur->left; } prev = NULL; while (!stk.empty()) { cur = stk.top(); stk.pop(); if (cur->right == prev) { // 右孩子不存在或已被访问,访问之 res.push_back(cur->val); prev = cur; // 更新刚访问的结点 } else { stk.push(cur); // 当前结点不能访问,需第二次入栈 cur = cur->right; // 先处理右子树 break; // 断开重新循环 } } } while (!stk.empty()); return res; } };
7. 中序遍历(非递归版本,不用栈, Morris遍历)
Morris遍历是基于线索二叉树的。
简单描述一下,二叉树中有一些度为1的节点和叶子节点,我们可以利用这些空缺的位置来进行线索化。
每当一个节点的左儿子为空时,我们将其左儿子指针指向该节点的前驱prev
每当一个节点的右儿子为空时,我们将其右儿子指针指向该节点的后继next
注意:前驱和后继这两个概念是基于中序遍历结果序列中的相对位置的
一个节点的前驱prev:该节点的左子树最右下节点(不一定为叶子节点,但其右儿子一定为空)
一个节点的后继next:该节点的右子树最左下节点(不一定为叶子节点,但其左儿子一定为空)
注意:线索化二叉树的过程中会修改原来二叉树的形态,我们在中序遍历的过程中,有个 修改右儿子(线索化,修改二叉树) 和 清零右儿子(恢复二叉树原态) 相对过程
class Solution { public: vector<int> inorderTraversal(TreeNode *root) { vector<int> res; TreeNode* curr = root; TreeNode* prev = NULL; while (curr != NULL) { if (curr->left == NULL) { res.push_back(curr->val); curr = curr->right; } else { prev = curr->left; while (prev->right != NULL && prev->right != curr) { prev = prev->right; // find the prev node of curr } if (prev->right == NULL) { prev->right = curr; // threading. modify the tree curr = curr->left; } else { prev->right = NULL; // clear thread. recover the tree res.push_back(curr->val); curr = curr->right; } } } return res; } };
8. 先序遍历(非递归版本,不用栈, Morris遍历)
class Solution { public: vector<int> preorderTraversal(TreeNode *root) { vector<int> res; TreeNode* curr = root; TreeNode* prev = NULL; while (curr != NULL) { if (curr->left == NULL) { res.push_back(curr->val); curr = curr->right; } else { prev = curr->left; while (prev->right != NULL && prev->right != curr) { prev = prev->right; // find the prev node of curr } if (prev->right == NULL) { res.push_back(curr->val); prev->right = curr; // threading. modify the tree curr = curr->left; } else { prev->right = NULL; // clear thread. recover the tree curr = curr->right; } } } return res; } };
9. 层次遍历(递归版本)
Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level).
For example:
Given binary tree {3,9,20,#,#,15,7}
,
3 / \ 9 20 / \ 15 7
return its level order traversal as:
[ [3], [9,20], [15,7] ]
/** * Definition for binary tree * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: vector<vector<int> > levelOrder(TreeNode *root) { vector<vector<int>> result; traverse(root, 1, result); return result; } void traverse(TreeNode* root, int level, vector<vector<int>>& result) { if (root == nullptr) return; if (level > result.size()) { result.push_back(vector<int>()); } result.at(level-1).push_back(root->val); traverse(root->left, level + 1, result); traverse(root->right, level + 1, result); } };
10. 层次遍历(非递归版本)
/** * Definition for binary tree * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: vector<vector<int> > levelOrder(TreeNode *root) { vector<vector<int>> result; if (root == nullptr) return result; queue<TreeNode*> current; queue<TreeNode*> next; current.push(root); vector<int> level; while (!current.empty()) { while (!current.empty()) { TreeNode* node = current.front(); current.pop(); level.push_back(node->val); if (node->left != nullptr) next.push(node->left); if (node->right != nullptr) next.push(node->right); } result.push_back(level); level.clear(); swap(current, next); } return result; } };