在学习二叉树相关知识之后,很多同学问到如何构建一颗二叉树?掌握了上文介绍的知识构建一颗二叉树其实很简单,只要保证每个节点的度不大于2,同时注意节点左右子节点的顺序,每个节点的数据结构除了保存数据元素外,分别定义指向左右节点的指针;按照以上这几项原则可以构建任意形状的二叉树。
本文将使用Java代码把一个数组转为二叉链表(注:这里构建的二叉树是完全二叉树);同时将使用递归和非递归两种方式,对二叉树按照前序、中序、后序方式进行遍历。
import java.util.Stack;
/**
1. @Title: 二叉树相关操作
2. @Description:
3. @Author: Uncle Ming
4. @Date:2017年1月6日 下午2:49:14
5. @Version V1.0
*/
public class BinaryTree {
//节点存储结构
static class Node {
String data; //数据域
Node left; //左节点
Node right; //右节点
Node(String data, Node left, Node right) {
this.data = data;
this.left = left;
this.left = left;
}
}
/**
* 通过数组构造一个二叉树(完全二叉树)
* @param array
* @param index
* @return
*/
static Node createBinaryTree(String[] array, int index) {
Node node = null;
if(index < array.length) {
node = new Node(array[index], null, null);
node.left = createBinaryTree(array, index * 2 + 1);
node.right = createBinaryTree(array, index * 2 + 2);
}
return node;
}
/**
* 中序遍历
* @param node 根节点
*/
static void inOrder(Node node) {
if(node == null) {
return;
}
inOrder(node.left);
System.out.print(node.data + ", ");
inOrder(node.right);
}
/**
* 前序遍历
* @param node 根节点
*/
static void preOrder(Node node) {
if(node == null) {
return;
}
System.out.print(node.data + ", ");
preOrder(node.left);
preOrder(node.right);
}
/**
* 后序遍历
* @param node 根节点
*/
static void postOrder(Node node) {
if(node == null) {
return;
}
postOrder(node.left);
postOrder(node.right);
System.out.print(node.data + ", ");
}
/**
* 非递归的前序遍历实现
* @param node
*/
static void iterativePreOrder(Node node) {
Stack<Node> nodeStack = new Stack<Node>();
nodeStack.push(node);
while(!nodeStack.isEmpty()) {
Node currentNode = nodeStack.pop();
System.out.print(currentNode.data + " ,");
if(currentNode.right != null) {
nodeStack.push(currentNode.right);
}
if(currentNode.left != null) {
nodeStack.push(currentNode.left);
}
}
}
/**
* 非递归的中序遍历实现
* @param node
*/
static void iterativeInOrder(Node node) {
Stack<Node> nodeStack = new Stack<Node>();
while(node != null) {
nodeStack.add(node);
while(node.left != null) {
node = node.left;
nodeStack.add(node);
}
while(node.right == null && !nodeStack.isEmpty()) {
node = nodeStack.pop();
System.out.print(node.data + ", ");
}
if(node != null) {
node = node.right;
}
}
}
/**
* 非递归的后序遍历
* @param node
*/
static void iterativePostOrder(Node node) {
Stack<Node> nodeStack = new Stack<Node>();
Node preNode = null; //记录上一个处理过的节点
while(true) {
//当前节点的左子树放入栈中
while(node.left != null) {
nodeStack.add(node);
node = node.left;
}
while(node.right == null || preNode == node.right) {
System.out.print(node.data + ", ");
preNode = node;
if(nodeStack.isEmpty()) { //终止条件
return;
}
node = nodeStack.pop();
}
//处理右节点
nodeStack.add(node);
node = node.right;
}
}
public static void main(String[] args) {
String[] array = {"A", "B", "C", "D", "E", "F", "G", "H"};
Node root = createBinaryTree(array, 0);
System.out.println("\n============非递归实现前序遍历结果=============");
iterativePreOrder(root);
System.out.println("\n============递归实现前序遍历结果=============");
preOrder(root);
System.out.println("\n============非递归实现中序遍历结果=============");
inOrder(root);
System.out.println("\n============递归实现中序遍历结果=============");
iterativeInOrder(root);
System.out.println("\n============非递归后序遍历结果=============");
iterativePostOrder(root);
System.out.println("\n============后序遍历结果=============");
postOrder(root);
}
}
代码的运行结果如下:
============非递归实现前序遍历结果=============
A ,B ,D ,H ,E ,C ,F ,G ,
============递归实现前序遍历结果=============
A, B, D, H, E, C, F, G,
============非递归实现中序遍历结果=============
H, D, B, E, A, F, C, G,
============递归实现中序遍历结果=============
H, D, B, E, A, F, C, G,
============非递归后序遍历结果=============
H, D, E, B, F, G, C, A,
============后序遍历结果=============
H, D, E, B, F, G, C, A,
小结:
1. 在根据数组构建二叉链表时,默认数组的逻辑结构反应了二叉树节点的父子关系。比如数组长度为n,索引i为节点i的编号,如果2i +1 ≤ n,则2i + 1 是节点i的左节点,否则节点i是叶子节点;如果2i + 2 ≤ n 则2i + 2是节点i的右节点,否则节点i无右节点;
2. 构建二叉树时重点是构建二叉树节点的存储结构;
3. 在遍历二叉树时使用了递归和非递归两种方式。通过上面的代码可以直观的发现递归方式的效率更高、代码更简洁。