1,个人感觉二叉树的实现主要还是如何构造一颗二叉树。构造二叉树函数的设计方法多种多样,本例采用 addNode 方法实现。以下程序通过定义内部类来表示二叉树的结点,然后再实现了二叉树这种数据结构的一些基本操作。
2,说说以下程序的一些不足:
a,56行中的判断树是否为空时,依据根结点的数据域是否为空来判断。而使用不带参数的构造函数构造二叉树时,根结点的不空的,此时说明树已经有了根结点,但是根结点的数据却是空的,此时的树高度为1,但是不能访问树根结点,因为树根结点的数据域没有值。
3,重点讲解下二叉树遍历的几个方法:
先序遍历:将先序遍历过程中遇到的结点添加到ArrayList<TreeNode>中。根据先序遍历的递归的性质,调用addAll(Container c)方法完成遍历主要过程。
层序遍历:层序遍历需要使用队列,方法level_Traverse 中定义了ArrayDeque<TreeNode> 类型的队列。
1 package tree; 2 3 import java.util.ArrayDeque; 4 import java.util.ArrayList; 5 import java.util.List; 6 import java.util.Queue; 7 8 public class BinaryTree<E> { 9 //为什么要用静态内部类?静态内部类中不能访问外部类的非静态成员 10 public static class TreeNode{ 11 // E data; 12 Object data; 13 TreeNode left; 14 TreeNode right; 15 public TreeNode(){ 16 17 } 18 public TreeNode(Object data){ 19 this.data = data; 20 } 21 //构造一个新节点,该节点以left节点为其左孩子,right节点为其右孩子 22 public TreeNode(Object data, TreeNode left, TreeNode right){ 23 this.data = data; 24 this.left = left; 25 this.right = right; 26 } 27 } 28 29 private TreeNode root;//实现二叉树的类的数据域,即根结点来表示二叉树 30 31 public BinaryTree(){ 32 this.root = new TreeNode(); 33 } 34 //以指定的根元素创建一颗二叉树 35 public BinaryTree(E data){ 36 this.root = new TreeNode(data); 37 } 38 39 //为指定的结点添加子结点,为什么要有addNode方法?因为给定一系列的结点,通过调用该方法来构造成一颗树 40 public TreeNode addNode(TreeNode parent, E data, boolean isLeft){ 41 if(parent == null) 42 throw new RuntimeException("父节点为空,无法添加子结点"); 43 if(isLeft && parent.left != null) 44 throw new RuntimeException("节点已经左子节点,添加失败"); 45 if(!isLeft && parent.right != null) 46 throw new RuntimeException("节点已经有右子节点,添加失败"); 47 TreeNode newNode = new TreeNode(data); 48 if(isLeft) 49 parent.left = newNode; 50 else 51 parent.right = newNode; 52 return newNode; 53 } 54 55 public boolean empty(){ 56 return root.data == null;//根据根元素判断二叉树是否为空 57 } 58 59 public TreeNode root(){ 60 if(empty()) 61 throw new RuntimeException("树空,无法访问根结点"); 62 return root; 63 } 64 65 public E parent(TreeNode node){ 66 return null;//采用二叉树链表存储时,访问父结点需要遍历整棵二叉树,因为这里不实现 67 } 68 69 //访问指定节点的左结点,返回的是其左孩子的数据域 70 public E leftChild(TreeNode parent){ 71 if(parent == null) 72 throw new RuntimeException("空结点不能访问其左孩子"); 73 return parent.left == null ? null : (E)parent.left.data; 74 } 75 public E rightChild(TreeNode parent){ 76 if(parent == null) 77 throw new RuntimeException("空结点不能访问其右孩子"); 78 return parent.right == null ? null : (E)parent.right.data; 79 } 80 81 public int deep(){ 82 return deep(root); 83 } 84 private int deep(TreeNode node){ 85 if(node == null) 86 return 0; 87 else if(node.left == null && node.right == null) 88 return 1; 89 else{ 90 int leftDeep = deep(node.left); 91 int rightDeep = deep(node.right); 92 int max = leftDeep > rightDeep ? leftDeep : rightDeep; 93 return max + 1; 94 } 95 } 96 97 /*二叉树的先序遍历,实现思想如下:树是一种非线性结构,树中各个结点的组织方式有多种方式 98 * 先序,即是一种组织方式。它将结点的非线性变成了按照某种方式组织成的线性结构 99 */ 100 //返回一个list,树中结点以先序的方式存放在该list中 101 public List<TreeNode> preTraverse(){ 102 return preOrderTraverse(root); 103 } 104 private List<TreeNode> preOrderTraverse(TreeNode node){ 105 List<TreeNode> list = new ArrayList<TreeNode>(); 106 list.add(node); 107 if(node.left != null) 108 list.addAll(preOrderTraverse(node.left));//递归的奇妙之处 109 if(node.right != null) 110 list.addAll(preOrderTraverse(node.right)); 111 return list; 112 } 113 114 //中序遍历 115 public List<TreeNode> inTraverse(){ 116 return inOrderTraverse(root); 117 } 118 private List<TreeNode> inOrderTraverse(TreeNode node){ 119 List<TreeNode> list = new ArrayList<TreeNode>(); 120 if(node.left != null) 121 list.addAll(inOrderTraverse(node.left)); 122 list.add(node); 123 if(node.right != null) 124 list.addAll(inOrderTraverse(node.right)); 125 return list; 126 } 127 128 //后序遍历 129 public List<TreeNode> postTraverse(){ 130 return post_Traverse(root); 131 } 132 private List<TreeNode> post_Traverse(TreeNode node){ 133 List<TreeNode> list = new ArrayList<TreeNode>(); 134 if(node.left != null) 135 list.addAll(post_Traverse(node.left)); 136 if(node.right != null) 137 list.addAll(post_Traverse(node.right)); 138 list.add(node); 139 return list; 140 } 141 142 //层序遍历 143 public List<TreeNode> levelTraverse(){ 144 return level_Traverse(root); 145 } 146 private List<TreeNode> level_Traverse(TreeNode node){ 147 Queue<TreeNode> queue = new ArrayDeque<TreeNode>(); 148 List<TreeNode> list = new ArrayList<TreeNode>();//按层序遍历定义的顺序将树中结点依次添加到数组列表中 149 if(root != null)//先将根结点入队列 150 queue.offer(root); 151 while(!queue.isEmpty())//队列不空时,说明遍历还未结束 152 { 153 list.add(queue.peek());//将队头元素添加到数组列表中 154 TreeNode p = queue.poll();//队头元素出队列 155 if(p.left != null) 156 queue.offer(p.left);//队头元素的左孩子入队列 157 if(p.right != null) 158 queue.offer(p.right);//队头元素的右孩子入队列 159 } 160 return list; 161 } 162 }
测试遍历的程序如下:
import java.util.ArrayList; import java.util.List; public class BinaryTreeTest { public static void main(String[] args) { BinaryTree<String> bt = new BinaryTree<String>("根节点"); BinaryTree.TreeNode tn1 = bt.addNode(bt.root(),"第二层左子结点", true); BinaryTree.TreeNode tn2 = bt.addNode(bt.root(), "第二层右子结点", false); BinaryTree.TreeNode tn3 = bt.addNode(tn2,"第三层左子结点",true); List<BinaryTree.TreeNode> list1 = new ArrayList<BinaryTree.TreeNode>(); list1 = bt.inTraverse(); System.out.println("inorder traverse"); for(BinaryTree.TreeNode node : list1) System.out.print(node.data + " "); List<BinaryTree.TreeNode> list2 = new ArrayList<BinaryTree.TreeNode>(); list2 = bt.preTraverse(); System.out.println("\n preorder traverse"); for(BinaryTree.TreeNode node : list2) System.out.print(node.data + " "); List<BinaryTree.TreeNode> list3 = new ArrayList<BinaryTree.TreeNode>(); list3 = bt.levelTraverse(); System.out.println("\n level traverse"); for(BinaryTree.TreeNode node : list3) System.out.println(node.data + " "); } }