您的位置:首页 > 理论基础 > 数据结构算法

大话数据结构(七)——二叉树创建与遍历(递归、非递归)的java实现

2017-05-30 11:05 417 查看
    什么是二叉树呢?二叉树是n(n>=0)个结点的有限组合,该集合或者为空集,或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。二叉树的具体结构如下图所示:



    二叉树的遍历这里介绍三种方法:前序遍历,中序遍历和后序遍历。

前序遍历:前序遍历的规则是若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再遍历右子树。若前序遍                     历上图中的树,则遍历顺序为:ABDECFG

中序遍历:中序遍历的规则是若二叉树为空,则空操作返回,否则根结点开始(但并不是先访问根结点),中序遍布根结点的左子                     树,然后是访问根结点,最后遍历右子树。若中序遍历上图中的树,则遍历顺序为:DBEAFCG

后序遍历:后序遍历的规则是若二叉树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根                     结点。若后序遍历上图中的树,则遍历顺序为:DEBFGCA

下面的程序实现了创建一棵二叉树,并使用了递归和非递归的方法对这个二叉树进行了前序遍历,中序遍历和后序遍历。

二叉树创建和遍历的实现方法

package BinaryTree;

import java.util.Stack;

/**
* Created by jiangxs on 17-5-29.
*/
public class BinaryTree {
//创建二叉树的结点
public class TreeNode<T>{
private T data;//结点数据
private TreeNode leftChild;//左孩子指针
private TreeNode rightChild;//右孩子指针

//空构造器
public TreeNode(){}

//含结点数据的构造器
public TreeNode(T data){
this.data = data;
}

//新结点含结点数据并以leftChild结点为左孩子,rightChild结点为右孩子
public TreeNode(T data,TreeNode leftChild,TreeNode rightChild){
this(data);
this.leftChild = leftChild;
this.rightChild = rightChild;
}

public T getData(){
return data;
}

public void setData(T data){
this.data = data;
}

public TreeNode getLeftChild(){
return leftChild;
}

public void setLeftChild(TreeNode leftChild){
this.leftChild = leftChild;
}

public TreeNode getRightChild(){
return rightChild;
}

public void setRightChild(TreeNode rightChild){
this.rightChild = rightChild;
}
}

/**
* 创建一棵二叉树
* A
* B C
* D E F G
*/
public TreeNode creatBinTree(){
//初始化各个结点,并对各个结点关联各自的左右孩子
TreeNode rootLeftLeft = new TreeNode("D",null,null);
TreeNode rootLeftRight = new TreeNode("E",null,null);
TreeNode rootRightLeft = new TreeNode("F",null,null);
TreeNode rootRightRight = new TreeNode("G",null,null);
TreeNode rootLeft = new TreeNode("B",rootLeftLeft,rootLeftRight);
TreeNode rootRight = new TreeNode("C",rootRightLeft,rootRightRight);
TreeNode root = new TreeNode("A",rootLeft,rootRight);
return root;
}

/**
* 前序遍历法
* 递归版本
*/
public void recursePreOrderTraverse(TreeNode root){
if (root == null)
return;
System.out.println("当前结点数据: "+root.getData());
recursePreOrderTraverse(root.leftChild);
recursePreOrderTraverse(root.rightChild);
}

/**
* 前序遍历法
* 非递归版本
*/
public void preOrderTraverse(TreeNode root){
//若根结点为空则直接结束
if (root == null)
return;
//初始化用于存放结点顺序的栈结构
Stack<TreeNode> stack = new Stack<TreeNode>();
//首先将根结点放入栈中一遍最初就弹出
stack.add(root);
while (!stack.isEmpty()){
//取出栈顶结点,并将其暂时存入temp
TreeNode temp = stack.pop();
System.out.println("当前结点数据: "+temp.getData());
//栈是后进先出结构,故先存入右子结点,再存入左子结点
if (temp.rightChild != null)
stack.push(temp.getRightChild());
if (temp.leftChild != null)
stack.push(temp.getLeftChild());
}
}

/**
* 中序遍历法
* 递归版本
*/
public void recurseInOrderTraverse(TreeNode root){
if (root == null)
return;
recurseInOrderTraverse(root.leftChild);
System.out.println("当前结点数据: "+root.getData());
recurseInOrderTraverse(root.rightChild);
}

/**
* 中序遍历法
* 非递归版本
*/
public void inOrderTranverse(TreeNode root){
if (root == null)
return;
Stack<TreeNode> stack = new Stack<TreeNode>();
//依靠root!=null进入循环
while (!stack.isEmpty() || root != null){
//如果根结点存在就将根节点压栈
if (root != null){
stack.push(root);
//更新根结点
root = root.leftChild;
}
else {
//如果根结点不存在就弹栈,使用temp保存弹栈元素
TreeNode temp = stack.pop();
//打印弹栈元素
System.out.println("当前结点数据: "+temp.getData());
//更新根结点
root = temp.rightChild;
}
}
}

/**
* 后序遍历法
* 递归版本
*/
public void recursePostOrderTraverse(TreeNode root){
if (root == null)
return;
recursePostOrderTraverse(root.leftChild);
recursePostOrderTraverse(root.rightChild);
System.out.println("当前结点数据: "+root.getData());
}

/**
* 后序遍历法
* 递归版本
*/
public void postOrderTraverse(TreeNode root){
if (root == null)
return;
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
//初始化打印过的结点
TreeNode printedNode = null;
while (!stack.isEmpty()){
//获取栈顶结点
root = stack.peek();
//如果一个结点的左子结点存在且该结点的左右子节点均未被打印,则说明这个结点是新的,将其压入栈
if (root.leftChild != null && printedNode != root.getLeftChild() && printedNode != root.getRightChild()){
stack.push(root.getLeftChild());
}
//如果一个结点左子结点为空或者已被打印,而且该结点的右子结点存在且未被打印,则将其压入栈中
else if (root.rightChild != null && printedNode != root.getRightChild()){
stack.push(root.getRightChild());
}
//若以上两个均不满足,说明该结点不能存在左右结点或者该结点的子结点均已被打印,则将该结点弹出并打印
else {
System.out.println("当前结点数据: "+stack.pop().getData());
//更新已被打印过的结点
printedNode = root;
}
}
}
}
测试代码
package BinaryTree;

/**
* Created by jiangxs on 17-5-29.
*/
public class BinaryTreeTest {
public static void main(String[] args) {
BinaryTree bt = new BinaryTree();
BinaryTree.TreeNode root = bt.creatBinTree();
System.out.println("----------递归前序遍历----------");
bt.recursePreOrderTraverse(root);
System.out.println("----------非递归前序遍历--------");
bt.preOrderTraverse(root);
System.out.println("----------递归中序遍历----------");
bt.recurseInOrderTraverse(root);
System.out.println("----------非递归中序遍历--------");
bt.inOrderTranverse(root);
System.out.println("----------递归后序遍历----------");
bt.recursePostOrderTraverse(root);
System.out.println("----------非递归后序遍历--------");
bt.postOrderTraverse(root);
}
}
测试结果
--
b36d
--------递归前序遍历----------
当前结点数据: A
当前结点数据: B
当前结点数据: D
当前结点数据: E
当前结点数据: C
当前结点数据: F
当前结点数据: G
----------非递归前序遍历--------
当前结点数据: A
当前结点数据: B
当前结点数据: D
当前结点数据: E
当前结点数据: C
当前结点数据: F
当前结点数据: G
----------递归中序遍历----------
当前结点数据: D
当前结点数据: B
当前结点数据: E
当前结点数据: A
当前结点数据: F
当前结点数据: C
当前结点数据: G
----------非递归中序遍历--------
当前结点数据: D
当前结点数据: B
当前结点数据: E
当前结点数据: A
当前结点数据: F
当前结点数据: C
当前结点数据: G
----------递归后序遍历----------
当前结点数据: D
当前结点数据: E
当前结点数据: B
当前结点数据: F
当前结点数据: G
当前结点数据: C
当前结点数据: A
----------非递归后序遍历--------
当前结点数据: D
当前结点数据: E
当前结点数据: B
当前结点数据: F
当前结点数据: G
当前结点数据: C
当前结点数据: A

Process finished with exit code 0

参考:《大话数据结构》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息