【Java数据结构】前序遍历、中序遍历、后续遍历图解以及算法
前序遍历、中序遍历、后续遍历图解以及算法
概述:
本文包含以下算法,大部分以递归实现
前序遍历(中左右):先输出自己,然后是左孩子,最后右孩子;
中序遍历(左中右):先输出左孩子,然后是自己,最后右孩子;
后序遍历(左右中):先输出左孩子,然后是右孩子,最后自己;
层次遍历 :一层一层从上到下从左到右遍历;
前序遍历和中序遍历确定一棵二叉树;
后序遍历和中序遍历确定一棵二叉树;
前序遍历和后续遍历其中一种加上中序遍历,便可以确定一颗二叉树;
1 public class Main { 2 public static void main(String[] args) { 3 /* 4 生成二叉树 5 0 6 1 2 7 3 4 5 6 8 前序遍历为: 0134256 9 中序遍历为: 3140526 10 后序遍历为: 3415620 11 */ 12 TreeNode[] tree = new TreeNode[7]; 13 for (int i = 0; i < 7; i++) { 14 tree[i] = new TreeNode(i); 15 } 16 17 for (int i = 0; i < 7; i++) { 18 if(i*2+1 < 7){ 19 tree[i].left = tree[i*2+1]; 20 } 21 if(i*2+2 <7){ 22 tree[i].right = tree[i*2+2]; 23 } 24 } 25 System.out.println("前序遍历的结果:"); 26 preOrder(tree[0]); 27 System.out.println("\n后序遍历的结果:"); 28 afterOrder(tree[0]); 29 System.out.println("\n中序遍历的结果:"); 30 infixOrder(tree[0]); 31 System.out.println("\n层次遍历的结果:"); 32 levelOrder(tree[0]); 33 System.out.println("\n二叉树的深度为:"); 34 System.out.println(getTreeDepth(tree[0])); 35 int[] pre = {0,1,3,4,2,5,6}; 36 int[] in= {3,1,4,0,5,2,6}; 37 int[] after = {3,4,1,5,6,2,0}; 38 Main main = new Main(); 39 TreeNode root1 = main.getTreeByPreAndInfix(pre,in); 40 System.out.println("重建后的树的中序遍历为:"); 41 infixOrder(root1); 42 TreeNode root2 = main.getTreeByAfterAndInfix(after,in); 43 System.out.println("\n重建后的树的中序遍历为:"); 44 infixOrder(root2); 45 46 } 47 48 /** 49 * 递归实现打印二叉树的前序遍历结果 50 * @param root 二叉树的根节点 51 */ 52 public static void preOrder(TreeNode root){ 53 if(root==null){ 54 return; 55 } 56 System.out.print(root.val+" "); 57 TreeNode left = root.left; 58 TreeNode right = root.right; 59 if(left != null){ 60 preOrder(left); 61 } 62 if(right != null){ 63 preOrder(right); 64 } 65 } 66 67 /** 68 * 递归实现打印二叉树的中序遍历结果 69 * @param root 二叉树的根节点 70 */ 71 public static void infixOrder(TreeNode root){ 72 if(root==null){ 73 return; 74 } 75 TreeNode left = root.left; 76 TreeNode right = root.right; 77 if(left != null){ 78 infixOrder(left); 79 } 80 System.out.print(root.val+" "); 81 if(right != null){ 82 infixOrder(right); 83 } 84 } 85 86 /** 87 * 递归实现打印二叉树的后序遍历 88 * @param root 二叉树的根节点 89 */ 90 public static void afterOrder(TreeNode root){ 91 if(root==null){ 92 return; 93 } 94 TreeNode left = root.left; 95 TreeNode right = root.right; 96 if(left != null){ 97 afterOrder(left); 98 } 99 if(right != null){ 100 afterOrder(right); 101 } 102 System.out.print(root.val+" "); 103 } 104 105 /** 106 * 层次遍历一个二叉树 107 * @param root 二叉树的根节点 108 */ 109 public static void levelOrder(TreeNode root){ 110 if(root==null){ 111 return; 112 } 113 LinkedList<TreeNode> list = new LinkedList<>(); 114 list.add(root); 115 TreeNode temp; 116 while (!list.isEmpty()){ 117 temp = list.poll(); 118 System.out.print(temp.val+" "); 119 if(temp.left!=null){ 120 list.add(temp.left); 121 } 122 if(temp.right!=null){ 123 list.add(temp.right); 124 } 125 } 126 127 } 128 129 HashMap<Integer,Integer> map = new HashMap<>(); 130 int[] pre; 131 /** 132 * 通过前序遍历和中序遍历确定一棵二叉树 133 * @param pre 前序遍历结果数组 134 * @param infix 中序遍历结果数组 135 */ 136 public TreeNode getTreeByPreAndInfix(int[] pre, int[] infix){ 137 this.pre = pre; 138 for (int i = 0; i < pre.length; i++) { 139 map.put(infix[i],i); 140 } 141 return recur(0,0,infix.length-1); 142 } 143 144 /** 145 * 前序和中序递归算法,思路是在中序遍历中确定左子树和右子树,同时确定左子树和右子树的根节点 146 * 根节点肯定是在前序遍历中找,但是找根节点需要确定左右子树的长度 147 * 左子树的根节点等于当前根节点+1 148 * 右子树的根节点等于当前根节点+左子树的长度+1 149 * 要确定长度,就要在中序遍历中找到上一个根节点的位置和树的左边界和右边界 150 * @param pre_root 前序遍历中根节点在数组中你的索引值 151 * @param in_left 当前树中序遍历中的左边界 152 * @param in_right 当前树中序遍历的有边界 153 * @return 根节点 154 */ 155 TreeNode recur(int pre_root,int in_left,int in_right){ 156 //如果这颗树的左边界大于右边界,证明上一个节点是叶子节点,直接返回空 157 if(in_left>in_right){ 158 return null; 159 } 160 //上一个节点不是叶子节点,则创建子树的根节点 161 TreeNode root = new TreeNode(pre[pre_root]); 162 //获得这个根节点在前序遍历中的下标 163 int i = map.get(pre[pre_root]); 164 //递归左子树,左子树的左边界和上一个节点的左边界一致,右边界为根节点在中序遍历中的下标减一 165 root.left = recur(pre_root+1,in_left,i-1); 166 //递归右子树,右子树的根节点在前序遍历中的下标为前一个根节点+左子树的长度(right-left)+1 167 root.right = recur(pre_root+(i-in_left)+1,i+1,in_right); 168 return root; 169 } 170 171 172 HashMap<Integer,Integer> map2 = new HashMap<>(); 173 int[] after; 174 175 /** 176 * 通过后序遍历和中序遍历确定一棵二叉树 177 * @param after 后序遍历结果数组 178 * @param infix 中序遍历结果数组 179 */ 180 public TreeNode getTreeByAfterAndInfix(int[] after,int[] infix){ 181 this.after = after; 182 for (int i = 0; i < infix.length; i++) { 183 map2.put(infix[i],i); 184 } 185 return recur2(after.length-1,0,after.length-1); 186 } 187 188 /** 189 * 在中序遍历中确定每一棵子树的根节点 190 * @param after_root 在后序遍历中根节点的下标 191 * @param in_left 在中序遍历中当前树的左边界 192 * @param in_right 在中序遍历中当前树的有边界 193 * @return 当前树的根节点 194 */ 195 TreeNode recur2(int after_root,int in_left,int in_right){ 196 //这棵子树的左边界大于有边界,证明这颗子树是叶子节点 197 if(in_left>in_right){ 198 return null; 199 } 200 //不是叶子节点,创建这颗子树的根节点 201 TreeNode root = new TreeNode(after[after_root]); 202 //获得当前根节点在中序遍历中的下标 203 int i = map2.get(after[after_root]); 204 //递归右子树,右子树的左边界在中序遍历中的下标是上一个根节点下标加一,右边界不变 205 root.right = recur2(after_root-1,i+1,in_right); 206 //递归左子树,左子树的根节点在后序遍历的位置=上一个的下标-右子树的长度-1,左边界不变,右边界等于上一个节点的下标-1 207 root.left = recur2(after_root-(in_right-i)-1,in_left,i-1); 208 return root; 209 } 210 211 /** 212 * 获得二叉树的深度 213 * @param root 二叉树的根节点 214 * @return 二叉树深度 215 */ 216 public static int getTreeDepth(TreeNode root){ 217 if(root ==null){ 218 return 0; 219 } 220 int left = getTreeDepth(root.left); 221 int right = getTreeDepth(root.right); 222 if(left>right){ 223 return left+1; 224 }else { 225 return right+1; 226 } 227 } 228 } 229 230 class TreeNode{ 231 public int val; 232 public TreeNode left; 233 public TreeNode right; 234 235 public TreeNode(int val) { 236 this.val = val; 237 } 238 }
分类:
Java数据结构
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构