本文旨在描述如何用迭代的方式一步一步实现二叉树前序遍历的过程!
我们先定义一个方法,参数为二叉树的根节点。方法内包含一个while循环。
private static void preOrderTraversal(TreeNode root) {
while(condition) {
}
}
还需要一个变量记录当前访问的节点,第一个访问的节点是根节点
private static void preOrderTraversal(TreeNode root) {
TreeNode cur = root;
while(condition) {
}
}
while循环的条件是什么呢?答案是还有需要访问的节点,也就是说当前访问的节点不为空。先不考虑,我们如何得到每次循环需要访问的节点。
private static void preOrderTraversal(TreeNode root) {
TreeNode cur = root;
while(cur != null) {
}
}
前序遍历是先访问根节点,所以我们可以在每次循环的开始,直接打印当前节点的值,代表已经访问过该节点
private static void preOrderTraversal(TreeNode root) {
TreeNode cur = root;
while(cur != null) {
system.out.print(cur.val + " ");
}
}
如果当前节点的左节点不为空,再访问当前节点的左节点。此时,当前节点的左节点变成当前节点。这是第一种我们得到每次循环需要访问节点的方法。
private static void preOrderTraversal(TreeNode root) {
TreeNode cur = root;
while(cur != null) {
system.out.print(cur.val + " ");
if(cur.left != null) {
cur = cur.left;
}
}
}
通过当前代码,我们最终会找到二叉树的最左节点。此时当前节点的左节点为空,如果当前节点的右节点不为空,接着访问当前节点的右节点。此时,当前节点的右节点变成当前节点。这是第二种我们得到每次循环需要访问节点的方法。
private static void preOrderTraversal(TreeNode root) {
TreeNode cur = root;
while(cur != null) {
system.out.print(cur.val + " ");
if(cur.left != null) {
cur = cur.left;
} else if(cur.right != null) {
cur = cur.right;
}
}
}
通过当前代码,我们最终会得到一个没有左节点,也没有右节点的节点。我们不再能通过第一种和第二种方法得到每次循环需要访问的节点。那是不是说循环就要结束了呢?当然不是,因为我们还没有遍历所有的节点。通过观察之前的循环,我们不难发现有部分节点我们只访问了他们的左节点,现在我们需要访问他们的右节点。但是由于二叉树没有指向父节点的引用,我们没有办法得到这些节点,所以我们需要将之前只访问过其左节点的那些节点保存起来。又由于我们访问这些节点的顺序跟之前循环的顺序正好相反,这符合栈先进后出的特性,所以我们使用栈来保存这些节点。
private static void preOrderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null) {
system.out.print(cur.val + " ");
if(cur.left != null) {
stack.push(cur);
cur = cur.left;
} else if(cur.right != null) {
cur = cur.right;
} else {
}
}
}
如果栈不为空,我们从栈顶弹出节点。如果该节点的右节点不为空,就访问该节点的右节点,并break当前循环。此时,该节点的右节点变成当前节点。这是第三种我们得到每次循环需要访问节点的方法。如果该节点的右节点为空,我们继续从栈顶弹出节点。
private static void preOrderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null) {
system.out.print(cur.val + " ");
if(cur.left != null) {
stack.push(cur);
cur = cur.left;
} else if(cur.right != null) {
cur = cur.right;
} else {
while(!stack.isEmpty()) {
TreeNode tmp = stack.pop();
if(tmp.right != null) {
cur = tmp.right;
break;
}
}
}
}
}
当然,如果栈是空的,那就表示没有需要访问的节点了。我们可以设置当前节点为空,或直接break主循环。还有一种情况,就是栈不为空,但是栈中所有节点的右节点都为空,此时也表示没有需要访问的节点了。我们也需要设置当前节点为空,或直接break主循环。这两种情况可以用一个逻辑来覆盖。
private static void preOrderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null) {
system.out.print(cur.val + " ");
if(cur.left != null) {
stack.push(cur);
cur = cur.left;
} else if(cur.right != null) {
cur = cur.right;
} else {
cur = null;
while(!stack.isEmpty()) {
TreeNode tmp = stack.pop();
if(tmp.right != null) {
cur = tmp.right;
break;
}
}
}
}
}
前序遍历
(1)访问根结点。
(2)前序遍历左子树。
(3)前序遍历右子树。