Leetcode: Binary Tree Preorder Traversal
Given a binary tree, return the preorder traversal of its nodes' values.
For example:
Given binary tree {1,#,2,3}
,
1 \ 2 / 3
return [1,2,3]
.
Note: Recursive solution is trivial, could you do it iteratively?
分析:迭代版先序遍历。用一个栈保存结点,压栈顺序为先右子树后左子树。时间复杂度为O(n),空间复杂度为O(h)。代码如下:
class Solution { public: vector<int> preorderTraversal(TreeNode *root) { vector<int> result; if(!root) return result; stack<TreeNode *> S; S.push(root); while(!S.empty()){ TreeNode *tmp = S.top(); result.push_back(tmp->val); S.pop(); if(tmp->right) S.push(tmp->right); if(tmp->left) S.push(tmp->left); } return result; } };
以上代码在leetcode中运行时间是32ms,下面是一个更快的版本用时8ms。但为什么会用时减少?理论上两种代码每个节点都是入栈和出栈一次,难道入栈出栈交替进行比入栈多次再出栈多次费时?
class Solution { public: vector<int> preorderTraversal(TreeNode *root) { vector<int> result; stack<TreeNode *> S; for(TreeNode *p = root; p; p = p->left){ S.push(p); result.push_back(p->val); } while(!S.empty()){ TreeNode * tmp = S.top(); S.pop(); for(TreeNode *p = tmp->right; p; p = p->left){ S.push(p); result.push_back(p->val); } } return result; } };
用栈的方法时间复杂度和空间复杂度皆为O(n),Morris遍历方法可以将空间复杂度降为O(1)。Morris遍历的主要思想是threaded binary tree,利用叶子节点的左右孩子指针,将其指向叶子节点在某种遍历中的前驱或后继。Morris遍历的过程分为两部分,一部分是线索化binary tree,另一部分是遍历tree。在线索化binary tree时,下一个处理的结点是当前节点的左孩子(如果有的话);在遍历tree的时候,下一个处理的节点是当前节点的右孩子。具体可参考这篇博文。Morris前序遍历在leetcode上运行时间是4ms,好于上面两种方法。
class Solution { public: vector<int> preorderTraversal(TreeNode *root) { vector<int> result; TreeNode *prev = NULL, *cur = root; while(cur != NULL){ if(cur->left == NULL){ result.push_back(cur->val); cur = cur->right; }else{ //find predecessor of cur prev = cur->left; while(prev->right != NULL && prev->right != cur){ prev = prev->right; } if(prev->right == NULL){//no threaded, so threaded now result.push_back(cur->val); prev->right = cur; cur = cur->left; }else{ prev->right = NULL; cur = cur->right; } } } return result; } };