常用的二叉树遍历方法有三种,第一种也是最容易想到的一种就是递归法,当递归到当前结点为空结点就退出递归,否则就先递归访问当前结点的左子结点,再访问当前结点,最后递归访问右子节点。
代码如下:
class Solution {
public:
void traverse(TreeNode *root, vector<int> &v){
//v被以引用的形式传递进来,对v的操作将会直接生效
if(root == nullptr)
;
//当递归到空结点时就什么也不做
else if(root->left == nullptr){
v.push_back(root->val);
traverse(root->right, v);
}
//某结点的左子结点为空,就可以访问它了
else{
traverse(root->left, v);
v.push_back(root->val);
traverse(root->right, v);
}
//其它情况需要先递归左子结点再访问本结点,最后递归右子结点
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> v; // v用来保存最后要依次访问的结点值
traverse(root, v); // 从根结点开始递归访问
return v;
}
};
递归法复杂度分析:
时间复杂度O(n),其中n为二叉树中结点的个数,二叉树中的每个结点只会被访问一次。
空间复杂度O(n),递归算法的空间复杂度取决于递归栈的深度,最坏情况下二叉树为一条单链,递归栈的深度会达到O(n)级别。
第二种方法,迭代法,迭代法需要用到栈以便存储未被访问且将来要被访问到的结点,每一个尚未被访问的结点都是以它为根结点的子树森林共同的根结点。先找到二叉树的最左结点,访问它之后再找到其右子树的最左结点,路径上经过的结点都将被入栈以便将来访问,重复这个过程直到栈为空且树中每个结点都入过栈,就能得到二叉树的中序遍历序列。
代码如下:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> v; //用来保存访问结果
stack<TreeNode*> s;
if(root == nullptr)
return v;
while(!s.empty() || root){
//如果保存结点的栈为不为空或者还有结点未入过栈则重复循环
while(root){
s.push(root);
root = root->left;
}
//找到二叉树的最左结点的左子结点为空,此时开始出栈
v.push_back(s.top()->val);
root = s.top()->right;
s.pop();
}
return v;
}
};
迭代法复杂度分析
时间复杂度O(n),n为树中结点个数,每个结点仅在出栈时被访问一次。
空间复杂度O(n), 同上,迭代法用到的栈在树为单链时需要的空间最大,为O(n)级别。
方法三,Morris中序遍历法
Morris方法的步骤如下:
1.(从根结点开始)如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。
2. 如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点(即当前结点左子树的最右节点)。
a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点(这一步的目的是为了访问完当前结点的左子树后能返回当前结点)。再将当前节点更新为当前节点的左孩子。
b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空(恢复树的形状)。输出当前节点。当前节点更新为当前节点的右孩子。
3. 重复以上1、2直到当前节点为空(见下图)。
(图片转自文章末尾大佬博客)
代码如下
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> v; //保存结果
TreeNode *prev = nullptr;
if(!root) //空树返回空vector
return v;
while(root){
if(!root->left){
v.push_back(root->val); //上述步骤一
root = root->right;
}
else{
prev = root->left;
//左子树的最右结点被设置为当前结点
while(prev->right != nullptr && prev->right != root){
prev = prev->right;
}
if(prev->right == nullptr){
prev->right = root;
root = root->left;
}
//否则恢复树形,此时以root为根结点的左子树已被遍历
else{
prev->right = nullptr;
v.push_back(root->val);
root = root->right;
}
}
}
return v;
}
};
时间复杂度:O(n),其中 n 为二叉排序树的节点个数。Morris 遍历中每个节点会被访问两次(寻找最右结点一次,遍历一次),因此总时间复杂度为 O(2n)=O(n)。
空间复杂度:O(1),程序原地运行,未借助其它内存空间。
图片地址:http://www.cnblogs.com/AnnieKim/archive/2013/06/15/MorrisTraversal.html