二叉树的前序、中序、后序遍历,通过递归和非递归的方式实现【Java】
概述
二叉树的遍历,一个老生常谈的问题,大学的时候就天天学习,但是那时候大多是了解每中遍历的方式,但是并没有用代码实现,工作之后,使用代码实现一下。
三种遍历方式的区别
具体的介绍我这里就不说了,只总结性的说一下,所谓的前序,中序,后序,的前中后,其实就是根节点被访问的前中后,前序遍历就是根节点在最开始,中序遍历就是根节点在中间,后序遍历就是根节点在最后,当然这个根节点并不一定是二叉树的唯一根节点,而是每个节点只要有子节点,那这个节点其实都可以看作子节点的根节点。
下面就上代码
通过java自己实现二叉树
先定义节点
1 package com.example.demo.tree; 2 3 /** 4 * @author steve 5 * @date 2020/4/18 3:16 下午 6 */ 7 public class Node<E> { 8 public Node<E> left; 9 public Node<E> right; 10 public Node<E> parent; 11 public E element; 12 public Node(E element){ 13 this.element = element; 14 } 15 }
定义二叉树
1 package com.example.demo.tree; 2 3 import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer; 4 import org.omg.PortableInterceptor.INACTIVE; 5 6 import java.util.Comparator; 7 8 /** 9 * @author steve 10 * @date 2020/4/16 10:03 上午 11 */ 12 public class BinaryTree<E> { 13 14 private int size; 15 public Node<E> root; 16 private Comparator<E> comparator; 17 18 public BinaryTree(Comparator<E> comparator){ 19 this.comparator = comparator; 20 } 21 22 public BinaryTree(){ 23 this(null); 24 } 25 26 public void add(E element){ 27 if (root == null){ 28 Node node = new Node(element); 29 root = node; 30 }else { 31 Node<E> parent = root; 32 Node<E> node = root; 33 int com = 0; 34 while (node != null){ 35 parent = node; 36 if (comparator == null){ 37 com = ((Comparable)node.element).compareTo(element); 38 }else { 39 System.out.println("-------------"); 40 com = comparator.compare(node.element,element); 41 } 42 if (com > 0){ 43 node = node.left; 44 }else if (com < 0){ 45 node = node.right; 46 }else { 47 node.element = element; 48 return; 49 } 50 } 51 Node<E> newNode = new Node(element); 52 if (com > 0){ 53 parent.left = newNode; 54 newNode.parent = parent.left; 55 }else{ 56 parent.right = newNode; 57 newNode.parent = parent.right; 58 } 59 } 60 size ++; 61 } 62 public boolean isEmpty(){ 63 return size == 0; 64 } 65 public int size(){ 66 return size; 67 } 68 69 public String toString() { 70 String d = root == null ? null : root.element + ""; 71 if (root == null){ 72 return "root:"+d; 73 }else { 74 String b = root.left == null ? null : root.left.element + ""; 75 String c = root.right == null ? null : root.right.element + ""; 76 return "root:"+d + ", left:" + b + ", right:"+ c; 77 } 78 79 } 80 81 82 public static void main(String[] args) { 83 //这种方式就是匿名内部类,通过给一个类传一个接口作为参数,然后这个通过一个匿名内部类是实现这个接口来传入实现。 84 BinaryTree<Integer> binaryTree = new BinaryTree<>(new Comparator<Integer>() { 85 @Override 86 public int compare(Integer o1, Integer o2) { 87 return o1 - o2; 88 } 89 }); 90 91 BinaryTree<Integer> binaryTree1 = new BinaryTree<>(); 92 binaryTree1.add(1); 93 binaryTree1.add(2); 94 binaryTree1.add(0); 95 System.out.println(binaryTree1.size()); 96 System.out.println(binaryTree.toString()); 97 } 98 }
遍历二叉树
1 package com.example.demo.tree; 2 3 import sun.tools.native2ascii.resources.MsgNative2ascii; 4 5 import java.util.*; 6 7 /** 8 * @author steve 9 * @date 2020/4/18 2:58 下午 10 * 遍历二叉树,分别使用递归和迭代进行前序中序后序遍历一颗二叉树 11 */ 12 public class TraversBinaryTree { 13 14 /** 15 * 前序遍历 16 * 从根节点开始,先遍历左子树,后遍历右子树 17 */ 18 public static void preOrderTraveral(Node<Integer> node){ 19 if (node == null) return; 20 System.out.println(node.element); 21 preOrderTraveral(node.left); 22 preOrderTraveral(node.right); 23 } 24 25 /** 26 * 前序遍历,不使用递归实现 27 * 解释:前序遍历是最简单的,因为这个是从头开始的,不用像中序遍历和后序遍历一样,都是从最低级的左孩子开始,所以这个,只需要一直入栈右孩子, 28 * 左孩子,就可以了,其实这个过程一遍下来之后,所有的左孩子都出栈了,剩下的就是右孩子,那再慢慢出栈就可以了。 29 * @param node 30 */ 31 public static void preOrderTraveralUseStack(Node<Integer> node){ 32 Stack<Node<Integer>> stack = new Stack<>(); 33 stack.push(node); 34 while (!stack.isEmpty()){ 35 Node<Integer> node1 = stack.pop(); 36 System.out.println(node1.element); 37 if (node1.right != null){ 38 stack.push(node1.right); 39 } 40 if (node1.left != null){ 41 stack.push(node1.left); 42 } 43 } 44 } 45 46 /** 47 * 中序遍历二叉树 48 * 根节点在中间,从最左边的节点开始,先左后中,后右,遍历 49 */ 50 public static void inOrderTraveral(Node<Integer> node){ 51 if (node == null) return; 52 inOrderTraveral(node.left); 53 System.out.println(node.element); 54 inOrderTraveral(node.right); 55 56 } 57 58 /** 59 * 中序遍历,不使用递归实现 60 * 解释:这个其实比后序遍历简单,因为,这个先压入中,之后压入左,在出栈的时候,可以直接按照这个顺序反着出栈,然后在这个过程中 61 * 只需要判断有没有右孩子,而不用担心右孩子重复入栈的问题,但是后序遍历就需要考虑这个问题 62 * @param node 63 */ 64 public static void inOrderTraveralUseStack(Node<Integer> node){ 65 if (node == null) return; 66 Stack<Node<Integer>> stack = new Stack<>(); 67 stack.push(node); 68 while (!stack.isEmpty()){ 69 if (node != null && node.left != null){ 70 stack.push(node.left); 71 node = node.left; 72 }else { 73 //已经弹出来的节点,其左孩子不能再入栈 74 node = stack.pop(); 75 System.out.println(node.element); 76 if (node.right != null){ 77 stack.push(node.right); 78 node = node.right; 79 }else { 80 node = null; 81 } 82 } 83 } 84 85 } 86 87 /** 88 * 后序遍历 89 * 根节点最后遍历,先左后右,之后中间的 90 */ 91 public static void postOrderTraveral(Node<Integer> node){ 92 if (node == null) return;; 93 94 postOrderTraveral(node.left); 95 postOrderTraveral(node.right); 96 System.out.println(node.element); 97 } 98 99 /** 100 * 后序遍历,不使用递归,使用栈的方式实现 101 * 1.先把左孩子压入栈,如果没有左孩子了,弹出左孩子 102 * 2.然后把右孩子压入栈,如果没有了,就弹出 103 * 注意: 104 * 1.这里要注意两个点,第一个点就是如果一个节点的左孩子和右孩子都已经判断完了,那么就把这个节点置为null 105 * 2.如果一个节点的右孩子已经处理过了,就不要重复入栈 106 * 总结: 107 * 1.不能重复入栈,无论是左孩子还是右孩子 108 * @param node 109 */ 110 public static void postOrderTraveralUseStack(Node<Integer> node){ 111 if (node == null) return; 112 Stack<Node<Integer>> stack = new Stack<>(); 113 stack.add(node); 114 Node<Integer> node2 = null; 115 while (!stack.isEmpty()){ 116 if (node != null && node.left != null){ 117 stack.push(node.left); 118 node = node.left; 119 }else { 120 node = stack.peek(); 121 if (node.right != null && node.right != node2){ 122 stack.push(node.right); 123 //这里为了防止右孩子重复入栈 124 node2 = node.right; 125 node = node.right; 126 }else { 127 stack.pop(); 128 System.out.println(node.element); 129 //这里为了防止右孩子重复入栈 130 node2 = node; 131 //这里为了防止左孩子重复入栈 132 node = null; 133 } 134 } 135 } 136 } 137 138 /** 139 * 层序遍历二叉树 140 * 使用队列实现,而不是栈 141 */ 142 public static void levelOrderTraveral(Node<Integer> node){ 143 Queue<Node<Integer>> queue = new LinkedList<>(); 144 queue.add(node); 145 while(!queue.isEmpty()){ 146 Node<Integer> node1 = queue.peek(); 147 queue.poll(); 148 System.out.println(node1.element); 149 if (node1.left != null){ 150 queue.add(node1.left); 151 } 152 if (node1.right != null){ 153 queue.add(node1.right); 154 } 155 } 156 } 157 158 public static void main(String[] args) { 159 BinaryTree<Integer> binaryTree = new BinaryTree<>(); 160 binaryTree.add(7); 161 binaryTree.add(4); 162 binaryTree.add(10); 163 binaryTree.add(9); 164 binaryTree.add(11); 165 binaryTree.add(5); 166 binaryTree.add(3); 167 System.out.println("------------preorder traveral use recursion---------"); 168 TraversBinaryTree.preOrderTraveral(binaryTree.root); 169 System.out.println("------------preorder traveral use stack"); 170 TraversBinaryTree.preOrderTraveralUseStack(binaryTree.root); 171 System.out.println("-------------inorder traveral use recursion-------------------"); 172 TraversBinaryTree.inOrderTraveral(binaryTree.root); 173 System.out.println("-------------inorder traveral use stack----------------"); 174 TraversBinaryTree.inOrderTraveralUseStack(binaryTree.root); 175 System.out.println("--------------postorder traveral use recursion------------------"); 176 TraversBinaryTree.postOrderTraveral(binaryTree.root); 177 System.out.println("--------------postorder traveral use stack-----------"); 178 TraversBinaryTree.postOrderTraveralUseStack(binaryTree.root); 179 System.out.println("--------------------------------"); 180 TraversBinaryTree.levelOrderTraveral(binaryTree.root); 181 } 182 183 }
总结
不知道是年龄大了,还是智商本身太低,通过非递归的方式实现二叉树的遍历,想了好久都没有思路,只有前序遍历是自己独立实现的,后面的中序和后序都是参考网上的代码,不过也许这个也是一个熟能生巧的事。