Java实现二叉树的创建和遍历操作(有更新)
博主强烈建议跳过分割线前面的部分,直接看下文更新的那些即可。
最近在学习二叉树的相关知识,一开始真的是毫无头绪。本来学的是C++二叉树,但苦于编译器老是出故障,于是就转用Java来实现二叉树的操作。但是二者原理是一致的,而且实现的方式也是大同小异!
下面就让我们来看看代码吧。
1、首先我们需要创建一个二叉树的节点类,便于我们对树的操作,当然了,你也可以在二叉树类的内部将节点类声明为内部类,但是这样会降低操作的灵活性。我才用的是单独创建一个BinaryTreeNode类,代码如下:
package MyBinaryTree;
public class BinaryTreeNode<T> {
T data;
BinaryTreeNode<T> leftChild;
BinaryTreeNode<T> rightChild;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
BinaryTreeNode() {
this.data = null;
this.leftChild = null;
this.rightChild = null;
}
BinaryTreeNode(T data) {
this.data = data;
this.leftChild = null;
this.rightChild = null;
}
public BinaryTreeNode(T data, BinaryTreeNode<T> leftChild,
BinaryTreeNode<T> rightChild) {
super();
this.data = data;
this.leftChild = leftChild;
this.rightChild = rightChild;
}
public BinaryTreeNode<T> getLeftChild() {
return leftChild;
}
public void setLeftChild(BinaryTreeNode<T> leftChild) {
this.leftChild = leftChild;
}
public BinaryTreeNode<T> getRightChild() {
return rightChild;
}
public void setRightChild(BinaryTreeNode<T> rightChild) {
this.rightChild = rightChild;
}
public boolean isLeaf() {
if (this.leftChild == null && this.rightChild == null) {
return true;
}
return false;
}
}
//我才用的是泛型定义,在C++中我们可以使用模板来实现相同的处理
2、有了节点类,下面就是二叉树类了,注释什么的在代码中已经非常详细了:
package MyBinaryTree;
import java.util.Queue;
import java.util.Stack;
import java.util.concurrent.LinkedBlockingQueue;
public class BinaryTree<T> {
private BinaryTreeNode<T> root;
public BinaryTree() {
BinaryTreeNode<T> node = new BinaryTreeNode<T>();
this.root = node;
}
public boolean isEmpty() {
if (root == null) {
return true;
}
return false;
}
public BinaryTreeNode<T> getRoot() {
return this.root;
}
public void CreateTree(BinaryTreeNode<T> node, T data) {
if (root == null) {
root = new BinaryTreeNode<T>();
} else {
if (Math.random() > 0.5) { //采用随机方式创建二叉树
if (node.leftChild == null) {
node.leftChild = new BinaryTreeNode<T>(data);
} else {
CreateTree(node.leftChild, data);
}
} else {
if (node.rightChild == null) {
node.rightChild = new BinaryTreeNode<T>(data);
} else {
CreateTree(node.rightChild, data);
}
}
}
}
/*
* 访问当前节点
*/
public void Visit(BinaryTreeNode<T> current) {
if(current!=null&¤t.getData()!=null){
System.out.println(current.getData());
}else{
System.out.println("null");
}
}
/*
* 广度优先遍历二叉树
*/
public void levelOrder(BinaryTreeNode<T> root) {
Queue<BinaryTreeNode<T>> queue = new LinkedBlockingQueue<BinaryTreeNode<T>>();
BinaryTreeNode<T> pointer = root;
/*
* 当前节点不为空时,放入队首
*/
if (pointer != null) {
queue.add(pointer);
}
/*
* 队列不为空时,先访问中间节点,访问完成后弹出队首节点;然后是左节点,再是右节点;
*/
while (!queue.isEmpty()) {
pointer = queue.peek();
Visit(pointer);
queue.remove();
if (pointer.leftChild != null) {
queue.add(pointer.leftChild);
}
if (pointer.rightChild != null) {
queue.add(pointer.rightChild);
}
}
}
/*
* 递归方式的前序遍历
*/
public void preOrder(BinaryTreeNode<T> root) {
Visit(root);
preOrder(root.leftChild);
preOrder(root.rightChild);
}
/*
* 非递归方式实现的前序遍历
*/
public void NPreOrder(BinaryTreeNode<T> root){
Queue<BinaryTreeNode<T>> queue=new LinkedBlockingQueue<BinaryTreeNode<T>>();
BinaryTreeNode<T> pointer=root;
/*
* 当前节点不为空,就一直放入队尾;当前节点为空时,访问队首元素,然后访问做孩子节点;然后弹出,再对新的队首元素进行判断
*/
while(!queue.isEmpty()||pointer!=null){
if(pointer!=null){
Visit(pointer);
if(pointer.rightChild!=null){
queue.add(pointer.rightChild);
}
pointer=pointer.leftChild;
}else{
pointer=queue.peek();
queue.remove();
}
}
}
/*
* 采用递归方式实现的中序遍历操作
*/
public void inOrder(BinaryTreeNode<T> root){
inOrder(root.leftChild);
Visit(root);
inOrder(root.rightChild);
}
/*
* 非递归方式实现的中序遍历
*/
public void NInOrder(BinaryTreeNode<T> root){
Stack<BinaryTreeNode<T>> stack=new Stack<BinaryTreeNode<T>>();
BinaryTreeNode<T> pointer=root;
/*
* 当前节点不为空,就一直进栈;当前节点为空时,访问栈顶元素,然后再访问右孩子节点
*/
while(!stack.isEmpty()||pointer!=null){
if(pointer!=null){
stack.push(pointer);
pointer=pointer.leftChild;
}else{
pointer=stack.peek();
Visit(pointer);
pointer=pointer.rightChild;
stack.pop();
}
}
}
/*
* 递归方式实现的后序遍历二叉树
*/
public void postOrder(BinaryTreeNode<T> root){
postOrder(root.leftChild);
postOrder(root.rightChild);
Visit(root);
}
/*
* 非递归方式实现的后序遍历二叉树
*/
public void NPostOrder(BinaryTreeNode<T> root){
Stack<BinaryTreeNode<T>> stack=new Stack<BinaryTreeNode<T>>();//初始化栈,用于保存带访问的节点
BinaryTreeNode<T> pointer=root; //保存根节点
BinaryTreeNode<T> preNode=root; //保存前一个被访问的节点
while(!stack.isEmpty()||pointer!=null){
//若当前节点不空,就一直进栈,然后继续向左走
while(pointer.leftChild!=null){
stack.push(pointer);
pointer=pointer.leftChild;
}
/*
* 当前节点为空时,分两种情况:
* 1、当前节点移动到栈顶处,然后访问栈顶元素的右节点
* 2、当前节点移动到栈顶,但是栈顶元素没有右节点,这就需要弹出栈顶元素,再对此元素访问;
* 然后再对新的栈顶元素进行判断即可
*/
while(pointer!=null&&(pointer.rightChild==null)||(pointer.rightChild==preNode)){
Visit(pointer);
preNode=pointer;
if(stack.isEmpty()){
return;
}
pointer=stack.peek();
stack.pop();
}
stack.push(pointer);
pointer=pointer.rightChild;
}
}
}
3、然后是我的测试类,下面请看代码:
public static void main(String[] args) {
BinaryTree<Integer> tree = new BinaryTree<Integer>();
for (int i = 1; i < 10; i++) {
tree.CreateTree(tree.root, i);
}
System.out.println("-----------下面是广度优先遍历二叉树--------------");
tree.levelOrder(tree.root);
System.out.println("-----------下面是非递归的前序遍历方式-------------");
tree.NPreOrder(tree.root);
System.out.println("-----------下面是非递归的中序遍历方式-------------");
tree.NInOrder(tree.root);
System.out.println("-----------下面是非递归的后序遍历方式-------------");
tree.NPostOrder(tree.root);
}
4、接下来是测试的结果:
-----------下面是广度优先遍历二叉树--------------
null
1
2
6
4
3
8
7
5
9
-----------下面是非递归的前序遍历方式-------------
null
1
6
2
3
4
5
7
8
9
-----------下面是非递归的中序遍历方式-------------
6
7
1
5
4
null
3
9
2
8
-----------下面是非递归的后序遍历方式-------------
7
6
5
4
1
9
3
8
2
null
5、不足之处:
也许是测试的时候方式不对,因为使用递归方式对二叉树进行遍历的时候会报出NullPointerException的空指针错误。如果你知道原因在哪?不妨写下你的评论。也好让我加以改正。
6、总结:
在学习的过程中我意识到了一点,希望与君共勉!那就是埋头敲代码是解决不了问题的。重要的是思路。没有思路,一味的测试也是不可能成功的。在敲代码之前,我们一定要搞懂我们要做什么,怎么做,这样才会事半功倍。希望能和大家共同学习,一起进步!
—————————时间的分割线——————————————————————–
2016年9月12日19:01:38
看到大二刚开始学数据结构的时候,写的这篇文章,水平真的是不忍直视啊。不过话又说回来了,编程的提高不就是这样一点一点积聚来的嘛。下面来写点比较容易理解的思路清晰的二叉树遍历相关的操作。
/**
* @Date 2016年9月12日
*
* @author 郭 璞
*
*/
package tree;
import java.util.Stack;
/**
* @author 郭 璞 <br>
* 二叉树的先序,中序,以及后序,递归以及非递归的实现
*
*/
public class FullScan {
public static void main(String[] args) {
Node head = createTree();
// recurseFront(head);
// recurseMid(head);
recurseEnd(head);
// front(head);
// mid(head);
endWith2Stack(head);
endWithOneStack(head);
}
/**
* 非递归实现的二叉树后序遍历<br>
* 借助于一个栈进行实现
*
* @param head
*/
public static void endWithOneStack(Node head) {
System.out.println();
if (head == null) {
return;
} else {
Stack<Node> stack = new Stack<Node>();
stack.push(head);
// 该节点代表已经打印过的节点,待会会及时的进行更新
Node printedNode = null;
while (!stack.isEmpty()) {
// 获取 栈顶的元素的值,而不是pop掉栈顶的值
head = stack.peek();
// 如果当前栈顶元素的左节点不为空,左右节点均未被打印过,说明该节点是全新的,所以压入栈中
if (head.getLeft() != null && printedNode != head.getLeft() && printedNode != head.getRight()) {
stack.push(head.getLeft());
} else if (head.getRight() != null && printedNode != head.getRight()) {
// 第一层不满足,则说明该节点的左子树已经被打印过了。如果栈顶元素的右节点未被打印过,则将右节点压入栈中
stack.push(head.getRight());
} else {
// 上面两种情况均不满足的时候则说明左右子树均被打印过,此时只需要弹出栈顶元素,打印该值即可
System.out.println("当前值为:" + stack.pop().getValue());
// 记得实时的更新打印过的节点的值
printedNode = head;
}
}
}
}
/**
* 非递归实现的二叉树的后序遍历<br>
* 借助于两个栈来实现
*
* @param head
*/
public static void endWith2Stack(Node head) {
System.out.println();
if (head == null) {
return;
} else {
Stack<Node> stack1 = new Stack<Node>();
Stack<Node> stack2 = new Stack<Node>();
stack1.push(head);
// 对每一个头结点进行判断,先将头结点放入栈2中,然后依次将该节点的子元素放入栈1.顺序为left-->right。便是因为后序遍历为“左右根”
while (!stack1.isEmpty()) {
head = stack1.pop();
stack2.push(head);
if (head.getLeft() != null) {
stack1.push(head.getLeft());
}
if (head.getRight() != null) {
stack1.push(head.getRight());
}
}
// 直接遍历输出栈2,即可实现后序遍历的节点值的输出
while (!stack2.isEmpty()) {
System.out.println("当前节点的值:" + stack2.pop().getValue());
}
}
}
/**
* 非递归实现的二叉树的中序遍历
*
* @param head
*/
public static void mid(Node head) {
System.out.println();
if (head == null) {
return;
} else {
Stack<Node> nodes = new Stack<Node>();
// 使用或的方式是因为 第一次的时候战中元素为空,head的非null特性可以保证程序可以执行下去
while (!nodes.isEmpty() || head != null) {
// 当前节点元素值不为空,则放入栈中,否则先打印出当前节点的值,然后将头结点变为当前节点的右子节点。
if (head != null) {
nodes.push(head);
head = head.getLeft();
} else {
Node temp = nodes.pop();
System.out.println("当前节点的值:" + temp.getValue());
head = temp.getRight();
}
}
}
}
/**
* 非递归实现的二叉树的先序遍历
*
* @param head
*/
public static void front(Node head) {
System.out.println();
// 如果头结点为空,则没有遍历的必要性,直接返回即可
if (head == null) {
return;
} else {
// 初始化用于存放节点顺序的栈结构
Stack<Node> nodes = new Stack<Node>();
// 先把head节点放入栈中,便于接下来的循环放入节点操作
nodes.add(head);
while (!nodes.isEmpty()) {
// 取出栈顶元素,判断其是否有子节点
Node temp = nodes.pop();
System.out.println("当前节点的值:" + temp.getValue());
// 先放入右边子节点的原因是先序遍历的话输出的时候左节点优先于右节点输出,而栈的特性决定了要先放入右边的节点
if (temp.getRight() != null) {
nodes.push(temp.getRight());
}
if (temp.getLeft() != null) {
nodes.push(temp.getLeft());
}
}
}
}
/**
* 递归实现的先序遍历
*
* @param head
*/
public static void recurseFront(Node head) {
System.out.println();
if (head == null) {
return;
}
System.out.println("当前节点值:" + head.getValue());
recurseFront(head.left);
recurseFront(head.right);
}
/**
* 递归实现的中序遍历
*
* @param head
*/
public static void recurseMid(Node head) {
System.out.println();
if (head == null)
return;
recurseMid(head.getLeft());
System.out.println("当前节点的值:" + head.getValue());
recurseMid(head.getRight());
}
/**
* 递归实现的后序遍历递归实现
*
* @param head
*/
public static void recurseEnd(Node head) {
System.out.println();
if (head == null)
return;
recurseEnd(head.getLeft());
recurseEnd(head.getRight());
System.out.println("当前节点的值为:" + head.getValue());
}
public static Node createTree() {
// 初始化节点
Node head = new Node(1);
Node headLeft = new Node(2);
Node headRight = new Node(3);
Node headLeftLeft = new Node(4);
Node headLeftRigth = new Node(5);
Node headRightLeft = new Node(6);
// 为head节点 赋予左右值
head.setLeft(headLeft);
head.setRight(headRight);
headLeft.setLeft(headLeftLeft);
headLeft.setRight(headLeftRigth);
headRight.setLeft(headRightLeft);
// 返回树根节点
return head;
}
}
class Node {
public int value;
public Node left;
public Node right;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
public Node() {
}
public Node(int value) {
this.value = value;
}
}
Java实现二叉树的创建和遍历操作(有更新)的更多相关文章
- c/c++二叉树的创建与遍历(非递归遍历左右中,破坏树结构)
二叉树的创建与遍历(非递归遍历左右中,破坏树结构) 创建 二叉树的递归3种遍历方式: 1,先中心,再左树,再右树 2,先左树,再中心,再右树 3,先左树,再右树,再中心 二叉树的非递归4种遍历方式: ...
- 二叉树的创建、遍历(递归和非递归实现)、交换左右子数、求高度(c++实现)
要求:以左右孩子表示法实现链式方式存储的二叉树(lson—rson),以菜单方式设计并完成功能任务:建立并存储树.输出前序遍历结果.输出中序遍历结果.输出后序遍历结果.交换左右子树.统计高度,其中对于 ...
- JS实现二叉树的创建和遍历
1.先说二叉树的遍历,遍历方式: 前序遍历:先遍历根结点,然后左子树,再右子树 中序遍历:先遍历左子树,然后根结点,再右子树 后续遍历:先遍历左子树,然后右子树,再根结点 上代码:主要还是利用递归 ...
- Java实现二叉树的创建、递归/非递归遍历
近期复习数据结构中的二叉树的相关问题,在这里整理一下 这里包含: 1.二叉树的先序创建 2.二叉树的递归先序遍历 3.二叉树的非递归先序遍历 4.二叉树的递归中序遍历 5.二叉树的非递归中序遍历 6. ...
- 二叉树的创建和遍历(C版和java版)
以这颗树为例:#表示空节点前序遍历(根->左->右)为:ABD##E##C#F## 中序遍历(左->根->右)为:#D#B#E#A#C#F# 后序遍历(左->右-> ...
- JAVA实现File类中的遍历操作并输出内容
package shb.java.testIo; import java.io.BufferedReader; import java.io.BufferedWriter; import java.i ...
- Java实现二叉树的构建与遍历
转载:http://ocaicai.iteye.com/blog/1047397 目录: 1.把一个数组的值赋值给一颗二叉树 2.具体代码 1.树的构建方法 2.具体代码 package tree; ...
- 剑指offer十七姊妹篇之二叉树的创建、遍历、判断子二叉树
1.二叉树节点类 public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public Tr ...
- 基于Java的二叉树的三种遍历方式的递归与非递归实现
二叉树的遍历方式包括前序遍历.中序遍历和后序遍历,其实现方式包括递归实现和非递归实现. 前序遍历:根节点 | 左子树 | 右子树 中序遍历:左子树 | 根节点 | 右子树 后序遍历:左子树 | 右子树 ...
随机推荐
- python学习之路web框架
WEB框架的本质 python的WEB框架分为两大类: 1.自己写socket,自己处理请求 2.基于wsgi(Web Server Gateway Interface WEB服务网关接口),自己处理 ...
- Unity中使用射线查询MeshCollider背面的方法
之前遇到一个问题要从MeshCollider背面方向发出射线,直至检测到该射线与MeshCollider的相交点为止. 后来我用双面MeshCollider的方法解决了http://www.cnblo ...
- Nginx之(三)Nginx配置
一个简单的配置文件如下: #定义Nginx运行的用户及用户组 user userName userGroupName; #工作进程数目,根据硬件调整,通常等于CPU数量或者2倍于CPU worker_ ...
- Java对象的内存布局以及对象所需内存大小计算详解
1. 内存布局 在HotSpot虚拟机中,对象的内存布局可以分为三部分:对象头(Header). 实例数据(Instance Data)和对齐填充(Padding). 1) 对象头(Header): ...
- 如何找出Xcode中不同版本Swift的路径
我们知道Xcode中可能包含不知一个Swift的版本,那么我们如何找到它们对应的路径呢? 熟悉unix shell命令的童鞋都知道有一个find指令,在我们已知Xcode路径时,我们可以在其中找到Sw ...
- Android的Intent机制详解
Intent 是一个消息传递对象,您可以使用它从其他应用组件请求操作.尽管 Intent 可以通过多种方式促进组件之间的通信,但其 基本用例主要包括以下三个: 启动 Activity: Activit ...
- ROS机器人程序设计(原书第2版)补充资料 (柒) 第七章 3D建模与仿真 urdf Gazebo V-Rep Webots Morse
ROS机器人程序设计(原书第2版)补充资料 (柒) 第七章 3D建模与仿真 urdf Gazebo V-Rep Webots Morse 书中,大部分出现hydro的地方,直接替换为indigo或ja ...
- Openresty 数据共享API.Data Sharing within an Nginx Worker
摘要自:https://github.com/openresty/lua-nginx-module/#data-sharing-within-an-nginx-worker 每nginx worker ...
- [ExtJS5学习笔记]第三十四节 sencha extjs 5 grid表格之java后台导出excel
继上次使用js前端导出excel之后,还有一个主要大家比较关注的是后台实现导出excel,因为本人开发使用的java所以这里使用apache的开源项目poi进行后台excel的导出. 本文目录 本文目 ...
- iOS开发之UIWebView的常见一些用法
虽然现在Xcode8已经开始使用WKWebView这个框架进行网页展示,但是UIWebView也有一些常用的方法需要知道,下面就简单展示一下,仅供大家参考 相关知识:1.设置背景透明:2.加载本地HT ...