非递归前序遍历
public List<TreeNode> perOrderTraversal(TreeNode root) {
// 判空
if (root == null) {
return null;
}
TreeNode node = root;
// 创建List保存返回结果
List<TreeNode> list = new ArrayList<>();
// 创建栈辅助遍历二叉树
Stack<TreeNode> stack = new Stack<>();
while (true) {
if (node != null) {
// 因为是前序遍历, 因此从根节点直接开始加入List中
list.add(node);
// 如果当前遍历到的节点有右子树, 将右子树入栈.
if (node.right != null) {
stack.push(node.right);
}
// 更改当前节点, 一直向左遍历, 直到遍历到上图节点H
node = node.left;
} else if (stack.isEmpty()) {
// 如果栈为空, 则证明遍历结束
return list;
} else {
// 走到这里说明node此时已经等于 节点H.left, 也就是说整棵二叉树左子树已经遍历完毕,
// 此时栈中元素为[C, E, G, I]
// 因此我们让 node = stack.pop() 即弹出栈顶元素I, 让右子树继续执行左子树执行过的操作
// 直到整棵二叉树遍历完毕, list中元素即为前序遍历结果.
node = stack.pop();
}
}
}
非递归中序遍历
public List<TreeNode> inOrderTraversal(TreeNode root){
// 判空
if (root == null) {
return null;
}
TreeNode node = root;
// 创建List保存返回结果
List<TreeNode> list = new ArrayList<>();
// 创建栈辅助遍历二叉树
Stack<TreeNode> stack = new Stack<>();
while (true) {
if (node != null) {
// 因为是中序遍历, 因此如果当前节点不为空, 便将其入栈
stack.push(node);
// 一直遍历左子树, 直到上图H节点
node = node.left;
} else if (stack.isEmpty()) {
// 如果栈为空, 则证明遍历结束
return list;
} else {
// 走到这里说明当前节点为null 且栈不为空, 即当前位于图中H.left.
// 栈中元素为[A, B, D, F, H] 因此我们弹出栈顶元素H , 将其加入结果集list
// 然后将当前遍历节点指向H.right, 若H节点有右子树, 则会进行同左子树相同的遍历操作
// 因为图中H元素没有右子树, 即node = null, 因此继续弹出栈中元素, 直到栈为空, 遍历结束
node = stack.pop();
list.add(node);
node = node.right;
}
}
}
非递归后序遍历
public List<TreeNode> postOrderTraversal(TreeNode root){
// 判空
if (root == null) {
return null;
}
TreeNode node;
// 创建prev节点, 用于保存上一次遍历的节点, 若上一次遍历的节点为当前节点的子节点
// 则需要遍历当前节点.
TreeNode prev = null;
// 创建List保存返回结果
List<TreeNode> list = new ArrayList<>();
// 创建栈辅助遍历二叉树
Stack<TreeNode> stack = new Stack<>();
// 将根节点入栈
stack.push(root);
// 若栈不为空则执行循环
while (!stack.isEmpty()) {
// 将node赋值为栈顶元素(栈顶元素不弹出)
node = stack.peek();
// 若当前元素为叶子节点或上一个元素为当前元素子节点
if (isLeaf(node) || isChild(node, prev)) {
// 将pop赋值为栈顶元素(弹出)
node = stack.pop();
// 更新prev
prev = node;
// 将当前遍历元素加入结果集
list.add(node);
// 当前元素还有左右子树, 将不为空的子树入栈(注意: 先入右子树, 后入左子树)
} else {
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
}
return list;
}
// 判断node是否为叶子节点
public boolean isLeaf (TreeNode node) {
return node.left == null && node.right == null;
}
// 判断node2是否为node1的子节点
public boolean isChild (TreeNode node1, TreeNode node2) {
if (node1 == null) {
return false;
}
return node2 == node1.left || node2 == node1.right;
}
以上面的后续遍历图为例:
①:将root入栈
栈中元素: [A]
②: 将root左右子树入栈
栈中元素: [A, C, B]
③: 将root更新为栈顶元素即 B, 将B左右子树入栈
栈中元素: [A, C, B, E, D]
④: 将B更新为栈顶元素即 D, 将D左右子树入栈
栈中元素: [A, C, B, E, D, G, F]
⑤: 将D更新为栈顶元素即 F, 将F左右子树入栈
栈中元素: [A, C, B, E, D, G, F, I, H]
⑥: 将F更新为栈顶元素即 H, 因为H为叶子节点, 因此将栈顶元素H弹出加入结果集, 将prev更新为H
栈中元素: [A, C, B, E, D, G, F, I]
⑦: 将H更新为栈顶元素即 I, 因为I为叶子节点, 因此将栈顶元素I弹出加入结果集, 将prev更新为I
栈中元素: [A, C, B, E, D, G, F]
⑧: 将I更新为栈顶元素即 F, 因为F不是叶子节点, 但是prev为I, 是F的子树, 因此将栈顶元素F弹出加入结果集, 将prev更新为F.
如果不添加子节点判断, 遍历到F时判断F不是叶子节点, 因此讲F的左右子树H, I加入栈中,就变成了死循环. 这就是添加prev保存前一个遍历元素的原因.