前序遍历
思路:
- 创建一个栈,从根节点开始
- 访问一个节点,这里传入了一个访问器把访问的实现留给用户,同时把右孩子节点入栈
- 继续向下访问左孩子节点,重复2直到访问完左子树(node==null)
- 如果栈非空,出栈一个节点,重复2
- 否则结束遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public void preorder(Visitor<E> visitor) { if(visitor == null || root == null){ return; } Node<E> node = root;
Stack<Node<E>> stack = new Stack<>();
while(true){ if(node!=null){ if(visitor.visit(node.element)) { return; } if(node.right!=null){ stack.push(node.right); } node = node.left; }else{ if(stack.isEmpty()){ return; } node = stack.pop(); } } }
|
中序遍历
思路:
- 创建一个栈
- 入栈当前节点,node = node.left
- 如果当前节点为空并且栈不为空,出栈一个节点,访问它并将node指向这个节点的右节点,重复2
- 否则遍历结束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public void inorder(Visitor<E> visitor) { if(visitor==null||root == null){ return; } Node<E> node = root; Stack<Node<E>> stack = new Stack<>(); while (true){ if(node!=null){ stack.push(node); node = node.left; }else { if(stack.isEmpty()){ return; } node = stack.pop(); if(visitor.visit(node.element)){ return; } node = node.right;
}
}
|
后序遍历
思路:
- 创建一个栈,将根节点入栈
- 如果栈不为空,peek栈顶元素
- 如果这个节点不是叶子节点也不是上一个出栈的叶子节点的父节点(prev),将这个节点的右孩子节点和左孩子节点入栈(前提是不为空)
- 否则出栈该节点,访问并维护prev指向,重复2,3
- 栈空遍历结束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public void postorder(Visitor<E> visitor) { if (visitor == null || root == null) { return; }
Stack<Node<E>> stack = new Stack<>(); stack.push(root); Node<E> top; Node<E> prev = null; while (!stack.isEmpty()) { top = stack.peek();
if (top.isLeaf() || (prev != null && prev.parent == top)) { prev = stack.pop(); if (visitor.visit(prev.element)) { return; } } else { if (top.right != null) { stack.push(stack.peek().right); } if (top.left != null) { stack.push(top.left); } }
} }
|
前序遍历的另一种方法
思路:
- 创建栈,根节点入队
- 如果栈不为空,出栈节点并访问
- 入栈节点右孩子节点和左孩子节点(前提不为空),重复2
- 栈空遍历结束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public void preorder2(Visitor<E> visitor) { if (visitor == null || root == null) { return; }
Stack<Node<E>> stack = new Stack<>(); stack.push(root); while (!stack.isEmpty()) { Node<E> node = stack.pop(); if(visitor.visit(node.element)){ return; } if(node.right!=null){ stack.push(node.right); } if(node.left!=null){ stack.push(node.left); }
}
|
总结:
总的来说使用非递归遍历树主要就是先到达叶子节点,然后处理边界问题,后序遍历稍特殊,需要判断是否为叶子节点来确保遍历所有节点。
- 这里的visotor没什么必要,这是用来在递归算法里标记是否结束