数据结构再学习之二叉树
一、树的基本知识与概念
根结点:一棵树最上面的结点。
父结点、子结点:如果一个结点下面连接了结点,称该结点为所连结点的父结点(又称双亲),所连结点为该结点的子结点(又称孩子)。
叶子结点:没有任何子结点的结点称为叶子结点。
结点的层:根结点的层定义为1,根结点的孩子为第2层,依此类推。
结点的度:结点拥有的子树的个数(叶子结点的度为0)。
树的度:树的所有结点的度数中最大的度数(上图中树的度为3,结点11的度为3,结点)。
树的深度(高度):树中最大的结点层数(上图中树的深度为4)。
森林:m(m>=0)棵互不相交的树的集合。删除一棵树的根结点就会得到一个森林,反之,若给一个森林增加一个统一的根结点,森林就变成一棵树。
二、二叉树基本知识与概念
1.二叉树的概念与性质
二叉树:每个结点最多两个有两个子树的树结构。若根结点有两个子树,则左边的为左子树,右边的为右子树。树的属性和概念同样适用于二叉树。
性质1:一个非空二叉树的第i层上至多有2^(i-1)个结点,其中(i>=1)。
性质2:深度(高度)为k的二叉树最多有2^k-1个结点,其中(k>=1);
2.特殊二叉树
满二叉树:每层都有最大数目结点的二叉树,即深度为k的满二叉树有2^k-1个结点,满二叉树如下图a所示。
(a)满二叉树 (b)完全二叉树
完全二叉树:对于深度为k的,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。换句通俗的话来说就是:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。完全二叉树如上图b所示。
满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树。
三、二叉树的存储结构
1.二叉树的顺序存储结构
顺序存储:是用一组地址连续的存储单元依次自上而下、自左往右地存储二叉树中的各个结点,个人直观理解:将二叉树中的结点按顺序存储。对于完全二叉树(包括满二叉树),则是将所有结点从上而下、自左往右的按顺序存储,换言之就是,将完全二叉树的结点自上而下、自左往右的依次进行编号,存储在数组中后,结点编号顺序与数组下标顺序一致。对于一般二叉树,先将其扩展为完全二叉树,然后对扩展的完全二叉树的结点进行编号,扩展增加的结点标记为“#”(实际存储时可用null或特定值),并按编号顺序存入数组。完全二叉树、一般二叉树的存储示例如下图。
顺序存储特点:比较适用于完全二叉树(包括满二叉树)的存储,用于一般二叉树的而言,显然会造成极大的空间浪费。
2.二叉树的链式存储
链式存储:将二叉树以链表的形式存储。二叉树的每个结点最多有2个孩子,把二叉树的结点作为链表的结点,在链表结点中除了对应二叉树结点的存储数据外,再增加2个(3个)指针域,分别指向左孩子、右孩子(左孩子、右孩子、双亲)。增加2个指针域构成的二叉树链式存储结构叫二叉链表(只能寻找孩子),增加3个指针域构成的二叉树链式结构叫三叉链表(可以寻找双亲和孩子)。
二叉树链式存储示意图如下:
四、二叉树的高度及遍历相关算法
1.求二叉树的高度
(1)递归求高度
输入:根结点 输出:树的高度 1. 求左子树的高度 2. 求右右子树的高度 3. 如果左子树高度>=右子树高度 4. 树的高度=左子树高度+1 5. 如果右子树的高度>左子树高度 6. 树的高度=右子树的高度+1 7. 返回树的高度
Java实现:
public int getHeight(LTreeNode<E> node){ //递归方法------获取二叉树的高度
if(node == null){
return 0;
}else{
int i = getHeight(node.getLchild());
int j = getHeight(node.getRchild());
return (i<j)?(j+1):(i+1);
}
}
(2)非递归求高度
利用广度优先搜所的方法,逐层访问树的结点。树的高度初始为0,每访问完一层,树的高度加1,在广度优先搜索时利用队列辅助访问。
输入:根结点root 输出:树的高度height 1. 初始化:辅助队列queue=null,树高度height=0,当前访问结点cur_node=null 2. queue.add(root),根结点root从尾部入对 3. while( 队列queue不为空 ) { 4. 获取队列长度(当前层的结点数):size=queue.size() 5. for(int i=0;i<size;i++){ //访问当前层的所有结点(从队列顶部访问结点并出队) 6. 队列顶部结点出队并赋值为当前访问结点(cur_node=queue.removeFirst()) 7. 如果当前访问结点的左孩子结点不为空,则将当前结点的左孩子结点从尾部入队 8. 如果当前访问结点的右孩子结点不为空,则将当前结点的右孩子结点从尾部入队 9. } 10. 树的高度加1(height++) 11. } 12. 返回树的高度
Java实现:
public int getHeight2(LTreeNode<E> node){ //非递归方法--------求二叉树高度
if(null==node){ return 0; }
LinkedList<LTreeNode> queue=new LinkedList<LTreeNode>(); //双端队列
int height=0;
queue.add(node); //linkedList.add()方法:从队列尾部插入元素
while(!queue.isEmpty()){
int size=queue.size();
for(int i=0;i<size;i++){ //遍历当前层的所有结点,将它们都出队(从头部),同时将它们的所有孩子都入队(从尾部)
LTreeNode<E> cur_node=queue.removeFirst(); //removeFirst()方法:移出队列头部的第一个元素
if(null!=cur_node.getLchild()){ //若左孩子不为空,则入队
queue.add(cur_node.getLchild());
}
if(null!=cur_node.getRchild()){ //若右孩子不为空,则入队
queue.add(cur_node.getRchild());
}
}
height++;
}
return height;
}
2.广度优先---层次遍历
利用辅助队列,逐层访问。
输入:根结点 输出:~~~ 1. 初始化:双端队列queue=null,当前访问结点cur_node=null,当前层结点数size=0 2. 根结点从队列尾部入队 3. while( 队列queue不为空 ){ 4. 当层结点数size=当前队列长度 5. for(int i=0;i<size;i++){ 6. 对列顶部结点出队,赋值给当前访问结点cur_node 7. 访问cur_node结点数据 8. 如果当前访问结点cur_node的左孩子不为空,将当前访问结点cur_node的左孩子从尾部入队 9. 如果当前访问结点cur_node的右孩子不为空,将当前访问结点cur_node的右孩子从尾部入队 10. } 11. }
Java实现:
public void LevelOrder(LTreeNode<E> node){ //广度优先:层次遍历
if(node!=null){
LinkedList<LTreeNode> queue=new LinkedList<LTreeNode>(); //双端队列
queue.add(node);
int height=0;
while(!queue.isEmpty()){
int size=queue.size(); //队列中元素的个数,即每层中结点个数
System.out.print("访问第"+height+"层结点:");
for(int i=1;i<=size;i++){
LTreeNode<E> cur_node=queue.removeFirst(); //removeFirst()移出队列头部的第一个元素
System.out.print(cur_node.getData()+" ");
if(null!=cur_node.getLchild()){ //若左孩子不为空,则入队
queue.add(cur_node.getLchild());
}
if(null!=cur_node.getRchild()){ //若右孩子不为空,则入队
queue.add(cur_node.getRchild());
}
}
height++;
System.out.println("");
}
}
}
3.深度优先---先序遍历
(1)递归方法
输入:根结点
输出:~~~ 1. 如果根结点不为空{ 2. 访问根结点的数据 3. 递归访问根结点的左子树 4. 递归访问根结点的右子树 5. }
Java实现:
public void PreOrder(LTreeNode<E> node){ //深度优先: 先序遍历-----递归实现
if(node!=null){
System.out.print(node.getData()+" ");
PreOrder(node.getLchild()); //左孩子不为空,则对左子树进行先序遍历
PreOrder(node.getRchild()); //右孩子不为空,则对右孩子进行先序遍历
}
}
(2)非递归方法
Java实现:
public void PreOrder_nonrecurrent(LTreeNode<E> node){ //深度优先: 先序遍历-----非递归实现
Stack<LTreeNode<E>> nodeStack = new Stack<LTreeNode<E>>();
LTreeNode<E> cur_node = node; //cur_node作为遍历指针
while(cur_node != null || !nodeStack.isEmpty()){ //当cur_node非空或栈非空时循环
if(cur_node != null){ //根指针非空,遍历左子树
nodeStack.push(cur_node); //根指针进栈
System.out.print(nodeStack.peek().getData() + " "); //根指针退栈,访问根节点
cur_node = cur_node.getLchild(); //每遇到非空二叉树先向左走
}else{ //再向右子树走
cur_node = nodeStack.pop();
cur_node = cur_node.getRchild();
}
}
}
4.深度优先---中序遍历
(1)递归方法
输入:根结点 输出:~~~ 1. 如果根结点不为空{ 2. 递归访问根结点的左子树 3. 访问根根结点的数据 4. 递归访问根结点的右子树 5. }
Java实现:
public void InOrder(LTreeNode<E> node){ //深度优先:中序遍历------递归实现
if(node != null){
InOrder(node.getLchild()); //递归遍历左子树
System.out.print(node.getData() + " ");
InOrder(node.getRchild()); //递归遍历右子树
}
}
(2)非递归方法
Java实现:
public void InOrder_nonrecurrent(LTreeNode<E> node){ //深度优先:中序遍历------非递归实现
Stack<LTreeNode<E>> nodeStack = new Stack<LTreeNode<E>>();
LTreeNode<E> cur_node = node; //cur_node作为遍历指针
while(cur_node != null || !nodeStack.isEmpty()){ //当cur_node非空或栈非空时循环
if(cur_node != null){ //根指针非空,遍历左子树
nodeStack.push(cur_node); //根指针进栈
cur_node = cur_node.getLchild(); //每遇到非空二叉树先向左走
}else{
cur_node = nodeStack.pop(); //根指针退栈,访问根节点
System.out.print(cur_node.getData() +" ");
cur_node = cur_node.getRchild(); //再向右子树走
}
}
}
5.深度优先---后序遍历
(1)递归方法
输入:根结点 输出:~~~ 1. 如果根结点不为空{ 2. 递归访问根结点的左子树 3. 递归访问根结点的右子树 4. 访问根结点的数据 5. }
Java实现:
public void PostOrder(LTreeNode<E> node){ //深度优先;后序遍历-----递归实现
if(node != null){
PostOrder(node.getLchild());
PostOrder(node.getRchild());
System.out.print(node.getData() + " ");
}
}
(2)非递归方法
Java实现:
public void PostOrder_nonrecurrent(LTreeNode<E> node){ //深度优先;后序遍历-----非递归实现
Stack<LTreeNode<E>> nodeStack = new Stack<LTreeNode<E>>();
LTreeNode<E> cur_node = node; //cur_node作为遍历指针
LTreeNode<E> pre_node = null; //表示最近一次访问的节点
while(cur_node != null || !nodeStack.isEmpty()){ //当cur_node非空或栈非空时循环
while(cur_node != null){ //一直向左走,遍历左子树
nodeStack.push(cur_node);
cur_node = cur_node.getLchild();
}
cur_node = nodeStack.peek();
if(cur_node.getRchild()==null || cur_node.getRchild() == pre_node){ //右子树为空或右子树已被访问时,该节点出栈
cur_node = nodeStack.pop();
System.out.print(cur_node.getData()+" ");
pre_node = cur_node; //将该节点赋值给最近一个访问节点
cur_node = null; //此处很重要,将刚出栈节点设置为空,对应于while循环的条件之一,否则陷入死循环
}else{
cur_node = cur_node.getRchild(); //遍历右子树
}
}
}
五、二叉树的实现(Java)
1.二叉树顺序存储实现
结点类:
public class BTreeNode<E> {
private E item;
private E leftSibling;
private E rightSibling;
BTreeNode(E item,E leftSibling, E rightSibling){//默认访问类型,只有同包才能调用其构造方法
this.setItem(item);
this.setLeftSibling(leftSibling);
this.setRightSibling(rightSibling);
}
public E getItem() {
return item;
}
public void setItem(E item) {
this.item = item;
}
public E getLeftSibling() {
return leftSibling;
}
public void setLeftSibling(E leftSibling) {
this.leftSibling = leftSibling;
}
public E getRightSibling() {
return rightSibling;
}
public void setRightSibling(E rightSibling) {
this.rightSibling = rightSibling;
}
}
二叉树接口:
public interface Tree<E> {
boolean isEmpty();
E getRoot();//获取根
E getParent(int nodeNum);//获取父结点
E getLeftSibling(int nodeNum);//获取左孩子
E getRightSibling(int nodeNum);//获取右孩子
BTreeNode<E> createNode(int headNum,E l, E r);//创建结点
BTreeNode<E> createHead(E item,E l, E r);//创建根结点
void breadFirstOrder();//广度优先
void preOrder();//先序遍历
void inOrder();//中序遍历
void postOrder();//后序遍历
void clear();//删除整个树
}
顺序二叉树类(实现了接口):
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
public class SqBTree<E> implements Tree<E> {
public Object[] elementData;//存储元素的数组
public int level;//层数 最高层为第0层
public BTreeNode<E> root;//根
private final static int DEFAULT_LEVEL = 10;//默认层数为10层
public SqBTree(int level){ //构造函数1 有参构造函数
elementData = new Object[(2<<level)-1];//使用位运算代替乘法,level层的数树最多有(2^level)-1个结点,2^level)-1=(2<<level)-1
this.level = level;
}
public SqBTree(){
this(DEFAULT_LEVEL);
} //构造函数2 无参构造函数
@Override
public boolean isEmpty() {
return elementData[0] == null;
}
@Override
public E getRoot() {
return (E) elementData[0];
}//获取根结点
@Override
public E getParent(int nodeNum) { //获取父亲结点
if(nodeNum%2 == 0){ //如果序号为偶数,父亲结点序号为(该结点序号/2 -1)
return checkIndex( (nodeNum>>1)-1 )? (E)elementData[(nodeNum>>1)-1] : null;
}
else{ //如果序号为奇数,父亲节点序号为 (该节点序号-1)/2
return checkIndex( (nodeNum-1)>>1 )?(E)elementData[(nodeNum-1)>>1]:null;
}
//checkIndex函数为:检查结点编号是否在数组的下标范围内,即判断该结点是否存在
}
@Override
public E getLeftSibling(int nodeNum) { //获取左孩子
return ( checkIndex(nodeNum) && checkIndex((nodeNum<<1)+1) ) ? (E)elementData[(nodeNum<<1)+1] : null;
}
@Override
public E getRightSibling(int nodeNum) { //获取右孩子
return ( checkIndex(nodeNum )&& checkIndex( (nodeNum+1)<<1) )?(E)elementData[(nodeNum+1)<<1]:null;
}
@Override
public void breadFirstOrder() {
/*
* 广度优先遍历
* 直接顺序访问存储数组
* */
String str = "";
for(int i = 0 ; i<elementData.length ; i++){
if(elementData[i] != null)
str +=elementData[i]+" ";
}
System.out.println(str);
}
@Override
public void preOrder() {
/*
* 先序遍历:
* 遇到一个结点,先访问该结点,在遍历左子树,将右子树压入栈中,直到左子树遍历完,然后开始弹栈,对弹栈出来的结点继续像之前一样遍历,直到栈为空
* */
Stack<Integer> dataStack = new Stack<Integer>();//用栈来存放节点编号
int currentNum = 0;
String str = "";
while(!dataStack.isEmpty()||elementData[currentNum]!=null){//栈不为空或者当前节点不为空
if(checkIndex(currentNum)&&elementData[currentNum]!=null){//当前节点不为空
str += (elementData[currentNum]+" ");//输出当前节点
if(checkIndex( (currentNum+1)<<1 )&& elementData[(currentNum+1)<<1]!=null)//如果有右孩子
dataStack.push((currentNum+1)<<1);//右孩子索引放入栈中
currentNum = (currentNum<<1)+1;//移向左孩子
}
else{
currentNum = dataStack.pop();//出栈 访问存入的右孩子
}
}
System.out.println(str);
}
@Override
public void inOrder() {
/*
* 中序遍历:
* 从根结点开始向左搜索,每遇到一个结点就把他压入栈中,然后取遍历这个结点的左子树,遍历完左子树后,开始弹栈,遍历弹出结点的右子树。
* */
Stack<Integer> dataStack = new Stack<Integer>();//用栈来存放节点编号
int currentNum = 0;
String str ="";
while(!dataStack.isEmpty()||elementData[currentNum]!=null){//栈不为空或者当前节点不为空
if(checkIndex(currentNum)&&elementData[currentNum]!=null){//当前节点不为空
dataStack.push(currentNum);//当前结点作为根节点入栈
currentNum = (currentNum<<1)+1;//转向左孩子
}
else{//节点为空了 当前路径访问到头了
currentNum = dataStack.pop();//出栈 访问存入的根结点
str += (elementData[currentNum]+" ");//访问根结点
currentNum = (currentNum+1)<<1;//当前结点切换为栈中根结点的右孩子
}
}
System.out.println(str);
}
@Override
public void postOrder() {
/*
* 后序遍历:
* 从根结点开始 向左搜索,每搜索到一个结点就将其压入栈中,直到栈中的结点不再有左子树为止。读取栈顶元素,如果该节点有右子树且未被访问,就访问其右子树,否则访问该节点并且弹栈
* */
Set<Integer> visitedSet = new HashSet<Integer>();//存放访问过的结点
int currentNum = 0;
Stack<Integer> dataStack = new Stack<Integer>();//用栈来存放编号
String str = "";
while(checkIndex(currentNum)&&elementData[currentNum]!=null){
while(checkIndex((currentNum<<1)+1)&&elementData[(currentNum<<1)+1]!=null){
dataStack.push(currentNum);//持续向左搜索 一旦遇到左结点为空 就停止搜索
currentNum = (currentNum<<1)+1;
}
//(当前结点不为空)且(没有右孩子或者右孩子被访问过了) 则访问该节点
while(checkIndex(currentNum) && elementData[currentNum]!=null &&
(!checkIndex(((currentNum+1)>>1)) || elementData[(currentNum+1)<<1]==null||
visitedSet.contains(elementData[(currentNum+1)<<1]))){
str += elementData[currentNum];
str +=" ";
visitedSet.add(currentNum);//添加进被访问过的集合
if(dataStack.isEmpty()){//栈空直接结束
System.out.println(str);
return;
}
currentNum = dataStack.pop();
}
dataStack.push(currentNum);
currentNum = (currentNum+1)<<1;//转向右子树
}
}
@Override
public void clear() {
root = null;
elementData = null;
level = 0;
}
public boolean checkIndex( int index ){ //检查index这个索引会不会导致数组越界
return index>=0&&index<=((2<<level)-2);//共level层时提供的编号的上下限
}
public void ensureCapacity(int level){//确保存储的数组有足够的容量
if(level>this.level){
if(elementData == null){
elementData = new Object[1];//为null就开辟一个
}
elementData = Arrays.copyOf(elementData, (2<<level)-1);//用copyOf来扩容
this.level = level;
}
}
@Override
public BTreeNode<E> createNode( int headNum,E l, E r) {
if(!checkIndex(headNum)||elementData[headNum]==null){//headNum没有
throw new IllegalArgumentException("头编号不存在");
}
if(l != null||r != null){//左右节点都有
if(checkIndex((headNum+1)<<1)&&level<10){//检查左右节点有没有足够level
ensureCapacity(10);//不到10层直接加到10层
}
else if(checkIndex((headNum+1)<<1)&&level>=10){//检查左右节点有没有足够level
ensureCapacity((headNum+1)<<1);//到了10层还不够则每次不够加一层
}
}
BTreeNode<E> tn = new BTreeNode<E>((E) elementData[headNum],l,r);//如果后面是null 直接创建
elementData[(headNum<<1)+1] = l;
elementData[(headNum+1)<<1]=r;
return tn;
}
@Override
public BTreeNode<E> createHead(E item, E l, E r) {
if(root!=null){
throw new IllegalArgumentException("已经有头了");
}
if(level<1){
ensureCapacity(10);
}
root = new BTreeNode<E>(item, l, r);
elementData[0] = item;
elementData[1] = l;
elementData[2] = r;
return root;
}
}
测试类:
public class test_main {
public static void main(String[] args) {
System.out.println("Tree test:");
SqBTree<Integer> at = new SqBTree<Integer>();
at.createHead(0, 1, 2);
at.createNode(1, 3, 4);
at.createNode(2, 5, 6);
at.createNode(3, 7, 8);
// at.breadFirstOrder();//0 1 2 3 4 5 6 7 8
// at.preOrder();//0 1 3 7 8 4 2 5 6
// at.inOrder();//7 3 8 1 4 0 5 2 6
// at.postOrder();//7 8 3 4 1 5 6 2 0
// System.out.println(at.level);
System.out.println(at.getParent(3));
System.out.println(at.getLeftSibling(0));
System.out.println(at.getRightSibling(-1));
at.clear();
at.createHead(1, 2, 3);
}
}
2.二叉树的链式存储实现
结点类:
public class LTreeNode<E> {
private E data; //数据
private LTreeNode<E> lchild; //左孩子
private LTreeNode<E> rchild; //右孩子
public LTreeNode(E e){ //无孩子的构造函数
this.data=e;
this.lchild=null;
this.rchild=null;
}
public LTreeNode(E e,LTreeNode<E> lchild,LTreeNode<E> rchild){ //有孩子的构造函数
this.data=e;
this.lchild=lchild;
this.rchild=rchild;
}
public E getData(){ return this.data; }
public void setData(E e){ this.data=e; }
public LTreeNode<E> getLchild() { return lchild; }
public void setLchild(LTreeNode<E> lchild) { this.lchild = lchild; }
public LTreeNode<E> getRchild() { return rchild; }
public void setRchild(LTreeNode<E> rchild) {this.rchild = rchild;}
}
二叉树类:
import java.util.LinkedList;
import java.util.Stack;
public class BTree<E> {
private LTreeNode<E> root;
public BTree(){this.root=null;} //无根构造函数,构造空树
public BTree(LTreeNode<E> root){ //有根构造函数
this.root=root;
}
public LTreeNode<E> getRoot() {
return root;
}
public void setRoot(LTreeNode<E> root) {
this.root = root;
}
public LTreeNode<E> getLeftChildTree(){ //获取左子树,返回左子树的根
if(this.root.getLchild()!=null){
return this.root.getLchild();
}else{
System.out.println("无左子树!");
return null;
}
}
public LTreeNode<E> getRightChildTree(){ //获取右子树,返回右子树的根
if(this.root.getRchild()!=null){
return this.root.getRchild();
}else{
System.out.println("无右子树!");
return null;
}
}
public int getHeight(LTreeNode<E> node){ //递归方法------获取二叉树的高度
if(node == null){
return 0;
}else{
int i = getHeight(node.getLchild());
int j = getHeight(node.getRchild());
return (i<j)?(j+1):(i+1);
}
}
public int getHeight2(LTreeNode<E> node){ //非递归方法--------求二叉树高度
if(null==node){ return 0; }
LinkedList<LTreeNode> queue=new LinkedList<LTreeNode>(); //双端队列
int height=0;
queue.add(node); //linkedList.add()方法:从队列尾部插入元素
while(!queue.isEmpty()){
int size=queue.size();
for(int i=0;i<size;i++){ //遍历当前层的所有结点,将它们都出队(从头部),同时将它们的所有孩子都入队(从尾部)
LTreeNode<E> cur_node=queue.removeFirst(); //linkedlist.removeFirst()方法:移出队列头部的第一个元素
if(null!=cur_node.getLchild()){ //若左孩子不为空,则入队
queue.add(cur_node.getLchild());
}
if(null!=cur_node.getRchild()){ //若右孩子不为空,则入队
queue.add(cur_node.getRchild());
}
}
height++;
}
return height;
}
public void LevelOrder(LTreeNode<E> node){ //广度优先:层次遍历
if(node!=null){
LinkedList<LTreeNode> queue=new LinkedList<LTreeNode>(); //双端队列
queue.add(node);
int height=0;
while(!queue.isEmpty()){
int size=queue.size(); //队列中元素的个数,即每层中结点个数
System.out.print("访问第"+height+"层结点:");
for(int i=1;i<=size;i++){
LTreeNode<E> cur_node=queue.removeFirst(); //linkedlist.removeFirst()方法:移出队列头部的第一个元素
System.out.print(cur_node.getData()+" ");
if(null!=cur_node.getLchild()){ //若左孩子不为空,则入队
queue.add(cur_node.getLchild());
}
if(null!=cur_node.getRchild()){ //若右孩子不为空,则入队
queue.add(cur_node.getRchild());
}
}
height++;
System.out.println("");
}
}
}
public void PreOrder(LTreeNode<E> node){ //深度优先: 先序遍历-----递归实现
if(node!=null){
System.out.print(node.getData()+" ");
PreOrder(node.getLchild()); //左孩子不为空,则对左子树进行先序遍历
PreOrder(node.getRchild()); //右孩子不为空,则对右孩子进行先序遍历
}
}
public void PreOrder_nonrecurrent(LTreeNode<E> node){ //深度优先: 先序遍历-----非递归实现
Stack<LTreeNode<E>> nodeStack = new Stack<LTreeNode<E>>();
LTreeNode<E> cur_node = node; //cur_node作为遍历指针
while(cur_node != null || !nodeStack.isEmpty()){ //当cur_node非空或栈非空时循环
if(cur_node != null){ //根指针非空,遍历左子树
nodeStack.push(cur_node); //根指针进栈
System.out.print(nodeStack.peek().getData() + " "); //根指针退栈,访问根节点
cur_node = cur_node.getLchild(); //每遇到非空二叉树先向左走
}else{ //再向右子树走
cur_node = nodeStack.pop();
cur_node = cur_node.getRchild();
}
}
}
public void InOrder(LTreeNode<E> node){ //深度优先:中序遍历------递归实现
if(node != null){
InOrder(node.getLchild()); //递归遍历左子树
System.out.print(node.getData() + " ");
InOrder(node.getRchild()); //递归遍历右子树
}
}
public void InOrder_nonrecurrent(LTreeNode<E> node){ //深度优先:中序遍历------非递归实现
Stack<LTreeNode<E>> nodeStack = new Stack<LTreeNode<E>>();
LTreeNode<E> cur_node = node; //cur_node作为遍历指针
while(cur_node != null || !nodeStack.isEmpty()){ //当cur_node非空或栈非空时循环
if(cur_node != null){ //根指针非空,遍历左子树
nodeStack.push(cur_node); //根指针进栈
cur_node = cur_node.getLchild(); //每遇到非空二叉树先向左走
}else{
cur_node = nodeStack.pop(); //根指针退栈,访问根节点
System.out.print(cur_node.getData() +" ");
cur_node = cur_node.getRchild(); //再向右子树走
}
}
}
public void PostOrder(LTreeNode<E> node){ //深度优先;后序遍历-----递归实现
if(node != null){
PostOrder(node.getLchild());
PostOrder(node.getRchild());
System.out.print(node.getData() + " ");
}
}
public void PostOrder_nonrecurrent(LTreeNode<E> node){ //深度优先;后序遍历-----非递归实现
Stack<LTreeNode<E>> nodeStack = new Stack<LTreeNode<E>>();
LTreeNode<E> cur_node = node; //cur_node作为遍历指针
LTreeNode<E> pre_node = null; //表示最近一次访问的节点
while(cur_node != null || !nodeStack.isEmpty()){ //当cur_node非空或栈非空时循环
while(cur_node != null){ //一直向左走,遍历左子树
nodeStack.push(cur_node);
cur_node = cur_node.getLchild();
}
cur_node = nodeStack.peek();
if(cur_node.getRchild()==null || cur_node.getRchild() == pre_node){ //右子树为空或右子树已被访问时,该节点出栈
cur_node = nodeStack.pop();
System.out.print(cur_node.getData()+" ");
pre_node = cur_node; //将该节点赋值给最近一个访问节点
cur_node = null; //此处很重要,将刚出栈节点设置为空,对应于while循环的条件之一,否则陷入死循环
}else{
cur_node = cur_node.getRchild(); //遍历右子树
}
}
}
}
测试类:
public static void main(String[] args) {
BTree<String> mytree = new BTree<String>();
LTreeNode<String> root = new LTreeNode<String>("A");
mytree.setRoot(root);
LTreeNode<String> rootL=new LTreeNode<String>("B");
LTreeNode<String> rootR=new LTreeNode<String>("C");
root.setLchild(rootL);
root.setRchild(rootR);
rootL.setLchild(new LTreeNode<String>("D") );
rootL.setRchild(new LTreeNode<String>("E"));
rootL.getLchild().setLchild(new LTreeNode<String>("H"));
rootL.getLchild().setRchild(new LTreeNode<String>("I"));
rootL.getRchild().setRchild(new LTreeNode<String>("J"));
rootR.setLchild(new LTreeNode<String>("F"));
rootR.setRchild(new LTreeNode<String>("G"));
rootR.getLchild().setLchild(new LTreeNode<String>("K"));
rootR.getRchild().setRchild(new LTreeNode<String>("L"));
int h1=mytree.getHeight(mytree.getRoot());
System.out.println("用递归算出的高度为:"+h1);
int h2=mytree.getHeight2(mytree.getRoot());
System.out.println("用非递归算出的高度为:"+h2);
mytree.LevelOrder(mytree.getRoot());
System.out.print("递归先序:");
mytree.PreOrder(mytree.getRoot());
System.out.print("\n非递归先序:");
mytree.PreOrder_nonrecurrent(mytree.getRoot());
System.out.print("\n递归中序:");
mytree.InOrder(mytree.getRoot());
System.out.print("\n非递归中序:");
mytree.InOrder_nonrecurrent(mytree.getRoot());
System.out.print("\n递归后序:");
mytree.PreOrder(mytree.getRoot());
System.out.print("\n非递归后序:");
mytree.PreOrder_nonrecurrent(mytree.getRoot());
}