JAVA数据结构开坑的第二天(二叉树)

开坑第二天,要完成的是二叉树。按字符串建树,完成先序递归/非递归的遍历。
目的字符串为:A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))
在这里插入图片描述

第一步:建树

实验分成两个java文件进行,一个包装功能函数,一个按顺序执行函数。

tree.java中的变量以及构造函数如下:

    tree left;
    tree right;
    char e;
    static int i=0;
    public tree(char e)
    {
        this.left=null;
        this.right=null;
        this.e=e;
    }

二叉树的建树,是先建立顶端节点然后初始化,然后建立新节点附着在上层节点的左或右。
建树我使用的是递归方式:

    public void create(char []a)
    {
        tree p=this;
        if(a[i]=='(')
        {    i++;
            if(a[i]==',')
                 i++;
        }
        if(a[i]==')'||a[i]==',')
        {
            i++;
            return;
        }
        if(a[i]>='A'&&a[i]<='Z')
        {
            if(a[i-1]!=',')
            {
                tree q=new tree(a[i]);
                p.left=q;
                i++;
                p.left.create(a);
            }
            if(a[i]==')'||a[i]==',')
            {
                i++;
                return;
            }
            tree o=new tree(a[i]);
            p.right=o;
            i++;
            p.right.create(a);
            if(a[i]==')'||a[i]==',')
                i++;
        }
    }

从图和字符串对应代码:若为*(则意味着开始(前字符的字节点建立,若(*后为字符,则为左节点,若为逗号加字符,则为右节点,若为字符加逗号加字符则意味着左右节点都有。当然存在左右括号包裹一个逗号,即无子节点,这部分在代码中我分开呈现,即没独立设置一个if去判断。
而逗号意味着先返回上级节点,然后判断有无字符,有则进入右节点无则返回。
右括号则证明需要返回到上一级的节点。

先序遍历

因为时间问题,只完成了先序遍历,通过递归和非递归的方式。

递归遍历

    public void show()//递归遍历 
    {
		System.out.print(this.e+" ");
        if(this.left!=null)
            this.left.show();
        if(this.right!=null)
            this.right.show();
    }

递归遍历很简单,输出-进入左节点-进入右节点,而修成中序遍历和后序遍历也很简单,只要修改输出就好不详谈。

非递归遍历
非递归遍历的思路很简单,输出节点,然后判断左右节点是否存在:
(1)左右节点均存在:存储右节点,进入左节点
(2)有左无右节点或有右无左节点:进入左节点/右节点
(3)左右节点为空:返回上一个节点。
1)若为左节点返回:判断右节点是否存在,存在则进入;
2)若为右节点返回:则再次往上返回

该非递归的难点在于,如何存放已输出的节点,因为存放的节点数未知,不可能建立巨大的数组存放,浪费空间。故我选择联合栈使用:
栈的结构为:

    tree num;
    stack next;
    int flag;
    public stack(){
        this.next=null;
    }
    public stack(tree e){
        this.next=null;
        this.num=e;
        flag=0;
    }

与最基础的栈相比,加入了int flag变量,用于判定是否有左右节点/左右节点进入了没

    public void show2()//非递归遍历
    {
        tree p=this;
        stack a=new stack();
        System.out.print(p.e+" ");
        do{
            if(p.left!=null||p.right!=null)
            {
                a.put(p);
                if(p.left!=null)
                    a.next.flag+=2;
                if(p.right!=null)
                    a.next.flag++;
            }
            else
                p=a.peek();
            if(a.next.flag>1)
            {
                p=p.left;
                a.next.flag-=2;
            }
            else if(a.next.flag>0)
            {
                p=p.right;
                a.next.flag--;
            }
            if(a.next.flag==0)
                a.pop();
            System.out.print(p.e+" ");
        }while(!a.empty()||p.left!=null||p.right!=null);
    }

我的思路是:先输出节点内容,然后判断是否存在左右节点。
若有左右节点,就证明必须再次查找该节点然后进入它的右节点,故放入栈中;哪怕只存在一边节点也无妨,为了令代码简略而设置这样。
而我设置的栈的flag变量,是用于判断在栈中的节点是否要加入右节点/已经进入了右节点。
我给该变量设置初值为0,若有左节点+2,若有右节点+1;即:

左右节点均在仅有左节点仅有右节点
321

然后设置进入左/右的条件,因为是先序遍历,所以必先进入左节点,由表格可得:
当进入左节点的时候,flag必大于1;进入右节点的时候flag等于1。故可用此控制进入左节点或者右节点,而当flag等于0的时候左右节点的进不去,故控制可达成。而当栈顶的flag等于0的时候,因为能进入必有节点,故证明此时它的节点均进入了,所以可以把栈顶舍弃了。
而循环的跳出条件也很重要,首先必须栈空,若栈未空证明栈中的节点的子节点未进入;而后面两个条件是防止指向栈中最后一个节点的右节点时,判定栈空,跳出循环,遍历失效。因为我编写的代码是,先指向栈顶的子节点,再进行判定,若栈顶的flag为0则出栈,这就导致存在误跳出循环的可能:指向了新的节点,但此时栈空。故加入新的判断条件,因为输出的最后一个节点,左右节点必为空,所以可以使用该条件同时判定。

P.S. 代码可能存在一点瑕疵,某意义上我是按照字符串制作的二叉树建树和遍历。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值