关于二叉树链式实现,常见的为如下两种:
- 二叉链表存储: 每个节点保留一个left,right域,指向左右孩子
- 三叉链表存储: 每个节点保留一个left, right, parent域,指向左右孩子和父亲
故对于每个二叉树的节点大致有如下的定义:
class Node{
T data;
Node left;
Node right;
Node parent; //非必要
}
不难看出对于该节点添加子节点非常容易,只需要让left或right指向子节点即可。
使用链式存储方式实现二叉树的简易代码如下:
public class LinkBinaryTree<T> {
public class TreeNode{
T data;
TreeNode left;
TreeNode right;
public TreeNode() {
}
public TreeNode(T data) {
this.data = data;
}
public TreeNode(T data, TreeNode left, TreeNode right) {
this.data = data;
this.left = left;
this.right = right;
}
@Override
public String toString() {
return "data:"+data+";left:"+left.data+";right:"+right.data;
}
}
private TreeNode root;
public LinkBinaryTree() {
this.root = new TreeNode();
}
//以指定根元素创建二叉树
public LinkBinaryTree(T data) {
this.root = new TreeNode(data);
}
/**
* 为指定节点添加子节点
* @param parent 需要添加子节点的父节点
* @param data 新节点的数据
* @param isLeft 是否为父节点的左孩子
* @return 新的节点
*/
public TreeNode addNode(TreeNode parent, T data, Boolean isLeft) {
TreeNode treeNode = new TreeNode(data);
if(parent == null) {
throw new RuntimeException("节点为null,无法添加子节点");
}
if(isLeft && parent.left !=null) {
throw new RuntimeException("已存在左孩子,无法添加");
}
if(!isLeft && parent.right !=null) {
throw new RuntimeException("已存在右孩子,无法添加");
}
TreeNode newNode = new TreeNode(data);
if(isLeft) {
parent.left = newNode;
}else {
parent.right = newNode;
}
return newNode;
}
public TreeNode root() {
return root;
}
//返回某个节点的父节点
public TreeNode parent(TreeNode temp) {
List<T> list = new ArrayList<T>();
Queue<TreeNode> stack = new LinkedList<TreeNode>();
stack.add(this.root());
while(!stack.isEmpty()) {
TreeNode node = stack.poll();
if(node.left == temp || node.right == temp) {
return node;
}
if(node.left!=null) {
stack.add(node.left);
}
if(node.right!=null) {
stack.add(node.right);
}
}
System.out.println("父节点不存在");
return null;
}
//返回某个节点的左孩子
public T left(TreeNode parent) {
if(parent == null) {
throw new RuntimeException("父节点为空");
}
return parent.left ==null ?null : parent.left.data;
}
//返回某个节点的右孩子
public T right(TreeNode parent) {
if(parent == null) {
throw new RuntimeException("父节点为空");
}
return parent.right ==null ?null : parent.right.data;
}
public int deep() {
return this.deep(this.root());
}
//获取某个节点的深度
//某个节点的深度为其最深子树的深度+1
public int deep(TreeNode node) {
if(node == null) {
return 0;
}
if(node.left == null && node.right == null) {
return 0;
}
int leftDeep = this.deep(node.left);
int rightDeep = this.deep(node.right);
int max = leftDeep>rightDeep? leftDeep: rightDeep;
System.out.println("max:"+max);
return max+1;
}
//先序遍历
public List<T> travPreRecursion() {
return this.preRecursion(this.root());
}
private List<T> preRecursion(TreeNode node) {
List<T> list = new ArrayList<T>();
list.add(node.data);
if (node.left!=null) {
list.addAll(preRecursion(node.left));// 左儿子
}
if (node.right!=null) {
list.addAll(preRecursion(node.right));// 右儿子
}
return list;
}
// 中序遍历(递归)
public List<T> travInRecursion() {
return this.inRecursion(this.root());
}
public List<T> inRecursion(TreeNode node) {
List<T> list = new ArrayList<T>();
if (node.left!=null) {
list.addAll(inRecursion(node.left));// 左儿子
}
list.add(node.data);
if (node.right!=null) {
list.addAll(inRecursion(node.right));// 右儿子
}
return list;
}
// 后序遍历(递归)
public List<T> travPostRecursion() {
return postRecursion(this.root());
}
public List<T> postRecursion(TreeNode node) {
List<T> list = new ArrayList<T>();
if (node.left !=null) {
list.addAll(postRecursion(node.left));// 左儿子
}
if (node.right !=null) {
list.addAll(postRecursion(node.right));// 右儿子
}
list.add(node.data);
return list;
}
}
现在根据该二叉树类来创建一个二叉树:
public static void linkBinaryTree() {
//首节点
LinkBinaryTree<String> binaryTree = new LinkBinaryTree<String>("a");
//首节点a的左子数为b
LinkBinaryTree<String>.TreeNode rootL = binaryTree.addNode(binaryTree.root(), "b", true);
//首节点a的左子数为c
LinkBinaryTree<String>.TreeNode rootR = binaryTree.addNode(binaryTree.root(), "c", false);
System.out.println(binaryTree.root());
LinkBinaryTree<String>.TreeNode rootLL = binaryTree.addNode(rootL, "d", true);
LinkBinaryTree<String>.TreeNode rootLR = binaryTree.addNode(rootL, "e", false);
System.out.println(rootL);
LinkBinaryTree<String>.TreeNode rootRL = binaryTree.addNode(rootR, "f", true);
LinkBinaryTree<String>.TreeNode rootRR = binaryTree.addNode(rootR, "g", false);
//获取树的深度
System.out.println(binaryTree.deep());
}
遍历
与之前介绍的顺序存储一样,对于迭代式遍历不做过多的介绍,重点在循环式遍历,至于具体的遍历过程与笔者的另一篇关于二叉树的顺序存储的实现和遍历中介绍的很情况,这里只介绍广度优先遍历
广度优先遍历即按二叉树的层来遍历,先第一层,第二层,…最后一层,比深度优先遍历简单代码也很容易的理解
已上一个binaryTree类为例:
public List<T> bfs(){
List<T> list = new ArrayList<T>();
//定义一个队列(先进先出) LinkedList可以作为队列使用
Queue<TreeNode> stack = new LinkedList<TreeNode>();
//首节点入队列
stack.add(this.root());
while(!stack.isEmpty()) {
//取队列首节点,
TreeNode node = stack.poll();
list.add(node.data);
//添加其孩子入队列
if(node.left !=null )
stack.add(node.left);
if(node.right!=null)
stack.add(node.right);
}
return list;
}
关于二叉树存储的实现,如果二叉树是完全二叉树,则建议使用顺序存储,顺序存储的二叉树进行广度优先遍历非常容易,但如果不是完全二叉树则使用顺序存储空间浪费较大,不建议。