数据结构再学习--二叉树1

数据结构再学习之二叉树

一、树的基本知识与概念

根结点:一棵树最上面的结点。

父结点、子结点:如果一个结点下面连接了结点,称该结点为所连结点的父结点(又称双亲),所连结点为该结点的子结点(又称孩子)。

叶子结点:没有任何子结点的结点称为叶子结点。

结点的层:根结点的层定义为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());
    }

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值