【数据结构与算法】二叉树的遍历与构造
写在前面
算法部分具体代码在github:GitHub - tod4-lab/Algorithms: Learning record of the book Algorithms(forth edition).,不定时进行学习更新
1 二叉树的遍历
递归写法比较常见,这里直接写非递归写法了。
1.1 二叉树的先序遍历
public void preOrderIter(TreeNode root) { if(root == null) return; Stack<TreeNode> s = new Stack<>(); s.push(root); while(!s.empty()) { TreeNode temp = s.pop(); System.out.println(temp.val); if(temp.right!=null) { s.push(temp.right); } if(temp.left!=null) { s.push(temp.left); } } }
非递归写法的大体思想是首先将根节点入栈,然后每次弹出栈顶节点,再将弹出节点的右节点、左节点依次入栈,因为栈先入后出的特性,根节点的左节点会成为栈顶在下一轮先出栈,由此实现根、左、右的先序遍历。
1.2 二叉树的中序遍历
public class InorderIter { public static void printInorderIter(BinaryTree.TreeNode root) { Stack<BinaryTree.TreeNode> s = new Stack<>(); while(root!=null) { s.push(root); root = root.left; } while(!s.empty()) { BinaryTree.TreeNode node = s.pop(); System.out.println(node.val); BinaryTree.TreeNode temp = node.right; while(temp!=null) { s.push(temp); temp = temp.left; } } } }
中序遍历的非递归写法的大体思想是,先将不断遍历子节点的左节点依次入栈,直到为空则每次先弹出根节点,然后再弹出右节点,再依次遍历子节点的左节点依次入栈。。。以此实现中序序列。
1.3 二叉树的后序遍历
后序遍历可以使用两个栈:一个栈进行根节点、右子树、左子树的顺序进行遍历,另一个栈负责将其逆序则可以得到后序序列。
public class PostOrderIter { public static void postOrderIterPrint(BinaryTree.TreeNode root) { if(root==null) return; Stack<BinaryTree.TreeNode> s1 = new Stack(); Stack<BinaryTree.TreeNode> s2 = new Stack(); s1.push(root); while(!s1.empty()) { BinaryTree.TreeNode temp = s1.pop(); s2.push(temp); if(temp.left!=null) s1.push(temp.left); if(temp.right!=null) s1.push(temp.right); } while(!s2.empty()) { System.out.println(s2.pop().val); } } }
非常巧妙的算法,据说后序遍历也能够用一个栈实现非递归算法,但是暂时没有想到怎么做,后序想到会回来填坑的
2 二叉树的构建
2.1 根据先序和中序构建二叉树
测试样例:
先序:3,9,20,15,7
中序:9,3,15,20,7
结果:3,9,20,null,null,15,7
二叉树结构:
public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } }
public TreeNode buildTree(int[] preorder, int[] inorder) { if(preorder.length == 0) return null; TreeNode root = new TreeNode(preorder[0]); int rootIndex = 0; while (inorder[rootIndex] != preorder[0]) { rootIndex++; } int len1 = rootIndex; int len2 = preorder.length - len1 - 1; TreeNode leftTree = null; TreeNode rightTree = null; int[] leftPreOrder = new int[len1]; int[] leftInOrder = new int[len1]; int[] rightPreOrder = new int[len2]; int[] rightInOrder = new int[len2]; if(len1!=0) { for (int i = 0; i < len1;i++) { leftInOrder[i] = inorder[i]; leftPreOrder[i] = preorder[i+1]; } leftTree = buildTree(leftPreOrder, leftInOrder); } if(len2!=0) { for (int i = 0; i < len2; i++) { rightInOrder[i] = inorder[i + rootIndex + 1]; rightPreOrder[i] = preorder[i + rootIndex + 1]; } rightTree = buildTree(rightPreOrder, rightInOrder); } root.left = leftTree; root.right = rightTree; return root; }
代码写的有点乱,大题思路是每次在先序序列中找到根节点并构建,然后根据根节点在中序序列的位置,能够确定左子树节点个数len1、右子树节点个数len2以及左右子树的划分。则递归进行对左右子树的构建,然后将根节点指针指向左右子树。
比较烦人的点就是数组下标的确定:
preOrder:左子树(1,len1) 右子树(len1+1,len1+len2)
inOrder:左子树(0,len1-1)右子树(len1,len1+len2)
分类:
数据结构与算法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步