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