首先来看下 recursive 的版本:
void inorder(TreeNode* node) {
if (node != NULL) {
inorder(node->left); //左子树
print(node->val); //当前节点
inorder(node->right); //右子树
}
}
如何将此版本转化成iterative的?
1. 参考Inorder Binary Tree Traversal (Iteratively)
说白了就是跑一遍程序,看看它是如何运行的。
比如
5
/ \
3 7
/ \ / \
1 4 6 8
中序:1,3,4,5,6,7,8
用TreeNode *p来代表当前处理的node。bool done代表是否完成。
如果当前节点p不为空,处理其左子树;如果为空,说明上轮迭代的p需要弹出来,因为左子树(为空)已经处理完了。(当然p为空也有可能是p指向的右子树为空,那说明p的整个函数都跑完了。)
弹出node之后,就要考虑它的右子树,所以把当前需要处理的节点设为弹出来的node的right,处理它的右子树。
请记住,被弹出栈的元素就被打印出来了,也就是已遍历了。
代码如下:
void inorder(TreeNode* root) {
stack<TreeNode*> st;
bool done = false;
TreeNode* p = root;
while (done == false) {
if (p != NULL) {
st.push(p);
p = p->left; //处理左子树
} else {
if (st.empty()) {
done = true;
break;
}
TreeNode* node = st.top();
print(node); //此节点
st.pop();
p = node->right; //处理右子树
}
}
}
2. 参考Help me understand Inorder Traversal without using recursion
stackoverflow中回答的第一条说,可以把recursion版的代码直接翻译成iteration版的代码
我们先做第一步:
把尾递归改成循环
void inorder(TreeNode* root) {
TreeNode* p = root;
while (p != NULL) {
inorder(p->left);
print(p);
p = p->right;
}
}
然后将中间的递归改写(用stack)
void inorder(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* p = root;
while (p != NULL || !st.empty()) {
if (p != NULL) {
st.push(p);
p = p->left;
} else {
TreeNode* node = st.top();
st.pop();
print(node);
p = node->right;
}
}
}
个人觉得这个
while (p != NULL || !st.empty())
其实也可以理解为原递归函数的两个出口就是
1. node 为 NULL
2. 栈帧空了
3. leetcode新题:
Binary Search Tree Iterator
此题将传统的中序遍历非递归 人为地拆开。
值得思考:
1. 如何拆
2. next()复杂度降到:平均时间O(1), 空间O(h), h为树高
3. 为什么平均时间为O(1), 空间为O(h)
4. c++ map的底层实现??
代码:
class BSTIterator {
private:
stack<TreeNode*> st;
TreeNode *node;
public:
BSTIterator(TreeNode *root) {
node = root;
while (node != NULL) {
st.push(node);
node = node->left;
}
}
/** @return whether we have a next smallest number */
bool hasNext() const{
if (node == NULL && st.empty()) return false;
return true;
}
/** @return the next smallest number */
int next() {
while (node != NULL) {
st.push(node);
node = node->left;
}
TreeNode *willpop = st.top();
st.pop();
node = willpop->right;
return willpop->val;
}
};
时间:降到O(1)的原因在于,遍历本身的时间是O(n),有n个节点,自然每个节点的时间是O(1)。空间:st的空间就是所用的存储空间,因此是树高,假设st一次开满的话,那么就要开树高那么多空间,就是说最大是树高。