构造一棵二叉树必须明确以下两种关系:
- 结点与其双亲结点及孩子结点间的层次关系。
- 兄弟结点间左或右的次序关系。
关系的确定:
- 先根遍历和后跟遍历反映双亲与孩子结点之间的层次关系。
- 中跟遍历反映兄弟结点之间的左右关系。
建立二叉树的方法:
- 按先根和中根次序遍历序列建立二叉树。
- 以标明空子树的先根次序遍历序列建立二叉树。
- 以广义表表示建立二叉树。
- 建立链式存储结构的完全二叉树。
PS:一个序列无法确定一个二叉树(特殊除外)。
1,前根中根还原二叉树
在二叉树类增加create()方法实现以先根遍历序列prelist[]、中根遍历序列inorder[]建立一颗二叉树的算法,并返回这颗二叉树的根结点。
【原理】先从前序的第一个结点开始,其为根节点,然后在中序中找到该元素,一分为二,中序左边为左子树,右边为右子树,然后从前序中找第二个元素为根结点左子树的根,然后重复上面这个过程,发现出现null,跳到右子树。
- 前根:ABDGCEFH
- 中跟:DGBAECHF
class Solution { public TreeNode buildTree(int[] preorder, int[] inorder) { return buildTree1(0, 0, preorder.length-1, preorder, inorder); } public TreeNode buildTree1(int preStart, int inStart, int inEnd, int[] preorder, int[] inorder) { if (inStart > inEnd || preStart > preorder.length) return null; int rootValue = preorder[preStart]; TreeNode root = new TreeNode(rootValue); int k = 0; for (int i = 0; i < preorder.length; i++) { if (inorder[i] == rootValue) { k = i; break; } } root.left = buildTree1(preStart + 1, inStart, k - 1, preorder, inorder); root.right = buildTree1(preStart + 1 + k - inStart, k + 1, inEnd, preorder, inorder); return root; } }
2,中根后根还原二叉树
【思想】先从后序中的最后一个开始,可知为根节点,然后根据A节点位置同时将中跟和后跟划分为左右子树,然后重新选择根节点,递归。
class Solution { public TreeNode buildTree(int[] middle, int[] postorder) { return buildTree2(middle,0,middle.length-1,postorder,0,postorder.length-1); } public static TreeNode buildTree2(int[] inorder, int inStart, int inEnd, int[] postorder, int postStart, int postEnd) { if (inStart > inEnd || postStart > postEnd) return null; int rootValue = postorder[postEnd]; TreeNode root = new TreeNode(rootValue); int k = 0; for (int i = 0; i < inorder.length; i++) { if (inorder[i] == rootValue) { k = i; break; } } root.left = buildTree2(inorder, inStart, k - 1, postorder, postStart, postStart + k - (inStart + 1)); root.right = buildTree2(inorder, k + 1, inEnd, postorder, postStart + k - inStart, postEnd - 1); return root; } }
3,以标明空子树的先根次序遍历序列建立二叉树(LeetCode 297)
仅有先根遍历不能唯一确定一颗二叉树。但是,如果在先根遍历中加入反映兄弟结点间的左右次序的信息(如以“^”标明空子树),则可以唯一确定一颗二叉树。
构造过程:设prelist表示一棵二叉树标明空子树的先根序列,构造算法如下:
- prelist[0]一定是二叉树的根,创建根结点; prelist[1]一定是根的左孩子。
- 若prelist[i]不是”^”,则创建一个结点,该结点的左孩子结点元素是prelist[i+1],但父母与孩子结点之间的链还未建立;否则当前子树为空,返回上一层结点。
- 返回到当前结点时,下一个元素prelist[i+1]是当前结点的右孩子结点;当一个结点的左右孩子链都已建立,则以当前结点为根的一棵子树就已建立,返回上一层结点。
- 重复执行步骤2~3,直到返回根结点,则二叉树建立,使root指向根结点。
public class Codec { String a = ""; public String serialize(TreeNode root) { if (root == null) { return ""; } preorderTraversal(root); return a; } public TreeNode deserialize(String data) { if (data.equals("")) { return null; } String[] f = data.split(" "); return create(f); } private int i = 0; private TreeNode create(String[] prelist) { TreeNode p = null; if (i < prelist.length) { String elem = prelist[i]; i++; if (!elem.equals("^")) { //不能elem!="∧",因为T不一定是String p = new TreeNode(Integer.parseInt(elem)); //创建叶子结点 p.left = create(prelist); //创建p的左子树,递归调用,实际参数与形式参数相同 p.right = create(prelist); //创建p的右子树,递归调用,实际参数与形式参数相同 } } return p; } public String preorderTraversal(TreeNode root) { if (root != null) { a += root.val + " "; preorderTraversal(root.left); //按先根次序遍历p的左子树,递归调用,参数为左孩子 preorderTraversal(root.right); //按先根次序遍历p的右子树,递归调用,参数为右孩子 } else { a += "^ "; } return a; } }
4,广义表表示建立二叉树
以广义表形式可以表示一棵树,但不能唯一表示一棵二叉树,因为无法明确左右子树。二叉树的广义表表示语法如下图,其中元素表示结点,“^”表示空子树。
输出二叉树的广义表表示
public void printGenList(){ //输出二叉树的广义表表示字符串 System.out.print("二叉树的广义表表示:"); printGenList(this.root); System.out.println(); } //输出以p结点为根的一棵子树的广义表表示字符串,先根次序遍历,递归算法 private void printGenList(BinaryNode<T> p){ if (p==null) //若二叉树空 System.out.print("∧"); //输出空子树标记 else{ System.out.print(p.data.toString()); //访问当前结点 if (p.left!=null || p.right!=null){ //非叶子结点,有子树 System.out.print("("); printGenList(p.left); //输出p的左子树,递归调用 System.out.print(","); printGenList(p.right); //输出p的右子树,递归调用 System.out.print(")"); } } }
按广义表次序遍历序列建立二叉树
public class BinaryTree_genlist{ private static int i=0; public static BinaryTree<String> createByGenList(String glist){ //以广义表表示构造二叉树 BinaryTree<String> bitree = new BinaryTree<String>(); i=0; if (glist.length()>0) bitree.root = create(glist); //创建子树,子树根值是glist[0] return bitree; } private static BinaryNode<String> create(String glist){ BinaryNode<String> p=null; char ch=glist.charAt(i); if (ch>='A' && ch<='Z'){ //大写字母 p = new BinaryNode<String>(ch+""); //创建叶子结点 i++; if (glist.charAt(i)=='('){ i++; //跳过'(' p.left = create(glist); //创建左子树,递归调用 i++; //跳过',' p.right = create(glist); //创建右子树,递归调用 i++; //跳过')' } } if (ch=='^') i++; //跳过'^' return p; //空串返回null } }
5,以完全二叉树的层次遍历序列建立二叉树
一棵具有n个结点的完全二叉树,对序号为i(0≤i<n)的结点,有:
- 若
,则
为根结点,无父母结点;
- 若i>0,则
的父母结点序号为
。
- 若
,则
的左孩子结点序号为
;否则
无左孩子。
- 若
,则
的右孩子结点序号为
;否则
无右孩子。
//二叉链表表示的完全二叉树类,继承二叉树类 public class CompleteBinaryTree<T> extends BinaryTree<T>{ public CompleteBinaryTree(){ //构造空二叉树 super(); } //以完全二叉树的层次序列构造完全二叉树,levellist指定层次序列 public CompleteBinaryTree(T[] levellist){ this.root = create(levellist, 0); } //创建以levellist[i]为根的一棵子完全二叉树,返回所建子树的根结点 private BinaryNode<T> create(T[] levellist, int i){ BinaryNode<T> p = null; if (i<levellist.length){ p = new BinaryNode<T>(levellist[i]); //创建叶子结点 p.left = create(levellist, 2*i+1); //创建p的左子树 p.right = create(levellist, 2*i+2); //创建p的右子树 } return p; } }