树的遍历方式汇总
如何遍历一棵树
有两种通用的遍历树的策略:
深度优先搜索(DFS)
在这个策略中,我们采用深度作为优先级,以便从跟开始一直到达某个确定的叶子,然后再返回根到达另一个分支。
深度优先搜索策略又可以根据根节点、左孩子和右孩子的相对顺序被细分为先序遍历,中序遍历和后序遍历。
宽度优先搜索(BFS)
我们按照高度顺序一层一层的访问整棵树,高层次的节点将会比低层次的节点先被访问到。
下图中的顶点按照访问的顺序编号,按照 1-2-3-4-5 的顺序来比较不同的策略。
本问题就是用宽度优先搜索遍历来划分层次:[[1], [2, 3], [4, 5]]
。
定义树的结点为:
struct TreeNode* { int val; TreeNode* left; TreeNode* right; TreeNode(int x):val(x),left(NULL),right(NULL){} };
(1)二叉树的前序遍历
方法1:递归
class Solution { public: vector<int>res; vector<int> preorderTraversal(TreeNode* root) { if(root==NULL) return res; res.push_back(root->val); preorderTraversal(root->left); preorderTraversal(root->right); return res; } };
方法2:迭代
从根节点开始,每次迭代弹出当前栈顶元素,并将其孩子节点压入栈中,先压右孩子再压左孩子。
在这个算法中,输出到最终结果的顺序按照 Top->Bottom 和 Left->Right,符合前序遍历的顺序。
///迭代 class Solution{ public: vector<int> preorderTraversal(TreeNode* root) { vector<int>res; if(root==NULL) return res; stack<TreeNode*>ss; ss.push(root); while(!ss.empty()) { TreeNode* temp=ss.top(); ss.pop(); res.push_back(temp->val); if(temp->right) ss.push(temp->right); if(temp->left) ss.push(temp->left); } return res; } };
(2)二叉树的中序遍历
方法1:递归
class Solution { private: vector<int>res; public: vector<int> inorderTraversal(TreeNode* root) { if(root==NULL) return res; inorderTraversal(root->left); res.push_back(root->val); inorderTraversal(root->right); return res; } };
方法2:迭代
class Solution { public: vector<int> inorderTraversal(TreeNode* root) { vector<int>res; if(root==NULL) return res; stack<TreeNode*>ss; TreeNode* cur=root; while(cur!=NULL || !ss.empty()) { while(cur!=NULL) { ss.push(cur); cur=cur->left; } cur=ss.top(); ss.pop(); res.push_back(cur->val); cur=cur->right; } return res; } };
(3)二叉树的后序遍历
方法1:递归
///递归 class Solution { public: vector<int> res; vector<int> postorderTraversal(TreeNode* root) { if(root==NULL) return res; postorderTraversal(root->left); postorderTraversal(root->right); res.push_back(root->val); return res; } };
方法2:迭代
LeetCode官方给的一种简直BUG的解法:从根节点开始依次迭代,弹出栈顶元素输出到输出列表中,然后依次压入它的所有孩子节点,按照从上到下、从左至右的顺序依次压入栈中。
因为深度优先搜索后序遍历的顺序是从下到上、从左至右,所以需要将输出列表逆序输出。
///迭代 class Solution{ public: vector<int> postorderTraversal(TreeNode* root) { stack<TreeNode*>ss; vector<int>res; if(root==NULL) return res; ss.push(root); while(!ss.empty()) { TreeNode* temp=ss.top(); ss.pop(); res.push_back(temp->val); if(temp->left) ss.push(temp->left); if(temp->right) ss.push(temp->right); } reverse(res.begin(),res.end()); return res; } };
另外一种迭代方式
class Solution { public: vector<int> postorderTraversal(TreeNode* root) { vector<int> ret; stack<TreeNode *> path; unordered_set<TreeNode *> _set; // 记录已经访问的结点 if (root) path.push(root); while (!path.empty()) { auto node = path.top(); bool leftVisited = true, rightVisited = true; // 左右结点判断先后顺序不能互换,因为需要先把右结点放进 stack中 if (node->right && _set.find(node->right) == _set.end()) { rightVisited = false; path.push(node->right); } if (node->left && _set.find(node->left) == _set.end()) { leftVisited = false; path.push(node->left); } if (leftVisited && rightVisited) { // 左右结点已经访问过了,才可以访问当前结点 ret.push_back(node->val); _set.insert(node); path.pop(); // 访问过了,从path中移除 } } return ret; } };
(3)二叉树的后序遍历
case1:分层次输出结果
给定二叉树: [3,9,20,null,null,15,7]
,
3 / \ 9 20 / \ 15 7
返回其层次遍历结果:
[ [3], [9,20], [15,7] ]
BFS需要借助队列啦
class Solution { public: vector<vector<int>>res; queue<TreeNode*>qq; vector<vector<int>> levelOrder(TreeNode* root) { if(root==NULL) return res; vector<int>vec; vec.push_back(root->val); res.push_back(vec); qq.push(root); while(qq.size()) { int cnt=qq.size(); vector<int>vec; for(int i=0;i<cnt;i++) { TreeNode* cur=qq.front(); qq.pop(); if(cur->left) { qq.push(cur->left); vec.push_back(cur->left->val); } if(cur->right) { qq.push(cur->right); vec.push_back(cur->right->val); } } if(!vec.empty())//vec不为空数组时才压入res; res.push_back(vec); } return res; } };