数据结构JAVA实现——树

数据结构java实现——树的双亲表示法

java实现树的双亲表示法
C++版

package com.treetest.test01;
import java.util.Arrays;
import java.util.Scanner;

//孩子双亲数组表示法
public class TreeOrder01 {
    public static void main(String[] args) {
        System.out.println("输入树的结点个数:");
        int num=new Scanner(System.in).nextInt();
        nodetree t=new nodetree(num);
        t.creatTree();
        System.out.println(t);
    }
}

class nodedata{//结点内容  +  双亲位置
    int parent;
    Object element;

    public nodedata() {}

    @Override
    public String toString() {
        /*return "nodedata{" +
                "parent=" + parent +
                ", element=" + element +
                '}';*///"parent"+"\t"+"\t"+"element"+"\t"
        return parent+"\t"+element+"\n";
    }
}

class nodetree extends nodedata{//树的结点
    nodedata[] data;
    //int root;
    int numchild;

    public nodetree(int num) {
        this.numchild=num;
        data=new nodedata[numchild];//这里是建立N个nodedata类型的数组  并没有new对象
        //this.root=-1;
    }

    public void creatTree(){
        System.out.println("根结点的父结点为-1,请输入后续结点内容和双亲位置:");
        System.out.println("结点个数:"+this.numchild);

        Scanner scanner=new Scanner(System.in);//下面输入的时候发现一个scanner,无法负责不同类型的输入
        Scanner scanner2=new Scanner(System.in);
        for (int i=0;i<this.numchild;i++){
            nodedata d=new nodedata();
            data[i]=d;
            if(i==0){
                System.out.println("这是根结点,其双亲结点下标为-1");
                data[i].parent=-1;
                System.out.println("输入根结点内容");
                data[i].element=scanner.nextLine();
            }else{
                System.out.println("输入第"+(i+1)+"个结点内容");
                data[i].element=scanner.nextLine();
                System.out.println("输入第"+(i+1)+"个结点parent位置");
                data[i].parent=scanner2.nextInt();
                while (data[i].parent>=i){
                    System.out.println("parent下标错误,请再输入:");
                    data[i].parent=scanner2.nextInt();
                }
            }

        }
    }
    public static String toStringshuzu(Object[] a){
        if (a == null)
            return "null";

        int iMax = a.length - 1;
        if (iMax == -1)
            return "[]";

        StringBuilder b = new StringBuilder();
        //b.append(' ');
        for (int i = 0; ; i++) {
            b.append(String.valueOf(a[i]));
            if (i == iMax)
                return b.toString();
            //b.append("\t");
        }
    }
    @Override
    public String toString() {
        System.out.println("parent"+"\t"+"element");
        return toStringshuzu(data);
    }
}

在这里插入图片描述

数据结构Java实现——树|N叉树之孩子双亲表示法——顺序存储结构+链表

C++版本如下
数据结构——树|N叉树之孩子双亲表示法——顺序存储结构+链表
数据结构Java实现——树|N叉树之孩子双亲表示法——顺序存储结构+链表(细节部分就不一一实现)

  • 孩子表示法
  • 数组+链表表示
  • 数组保存所有的结点模式,链表保存其相关的孩子结点 构成单链表
    一个结点元素: 内容+孩子指针个数n+n个孩子指针
  • 那么涉及到三个结点
  • 01第一个孩子的兄弟指针
  • 02结点结构:元素+双亲下标+第一个孩子指针
  • 03 树的整体结构 结点个数+根结点下标+各个结点的结构(指向上一级结点)
package com.treetest.test01;

import java.util.Arrays;
import java.util.Scanner;

/*
* 孩子表示法
* 数组+链表表示
* 数组保存所有的结点模式,链表保存其相关的孩子结点  构成单链表
*一个结点元素: 内容+孩子指针个数n+n个孩子指针
* 那么涉及到三个结点
* 01第一个孩子的兄弟指针
* 02结点结构:元素+双亲下标+第一个孩子指针
* 03 树的整体结构 结点个数+根结点下标+各个结点的结构(指向上一级结点)
* */
public class TreeLink01 {
    public static void main(String[] args) {
        TreeNode t=new TreeNode(9);
        t.creatrootnode();
        t.Addnode();
        System.out.println(t);
    }
}

/*孩子结点
* 孩子可以指向同双亲的其他孩子指针
* */
class ChildNode{
    int child;//用来存储 某个孩子结点 在其父结点下的下标
    ChildNode next;//用来存储同一结点的下一孩子结点
}
/*表头结构
* 每个结点有三部分
* 元素
* 保存双亲信息
* 第一个孩子指针
* */
class BoxNode{
    Object data;
    int parent;
    ChildNode firstchild;

    @Override
    public String toString() {
        return parent+"\t"+"    "+data+"\n";
    }
}
/*树结构
* 结点个数  --->数组结构
* 根结点双亲位置 设定 -1
* */
class TreeNode{
    BoxNode[] nodes;
    int roon,numnode;

    public TreeNode(int numnode) {
        this.numnode = numnode;
        nodes=new BoxNode[this.numnode];
        System.out.println("根结点的双亲位置指向-1");
        this.roon=-1;
    }

    /*创建一个根结点*/
    public void creatrootnode(){
        BoxNode b=new BoxNode();
        nodes[this.roon+1]=b;
        System.out.println("请根结点赋值:");
        nodes[this.roon+1].data= new Scanner(System.in).nextLine();
        //根结点赋值 之后 要构造其与双亲的关系
        nodes[this.roon+1].parent=this.roon;//根结点 指向-1 双亲
        //该方法只构造一个根结点,所以孩子皆为空
        nodes[this.roon+1].firstchild=null;
        /*其他结点先设定为null*/
       /* for (int i = this.roon+2; i <this.numnode ; i++) {
            BoxNode b2=new BoxNode();
            nodes[i]=b2;
            nodes[i].data=null;
            nodes[i].firstchild=null;
            //nodes[i].parent=0;
        }*/
    }
    /*向已经建立根结点的树添加其他结点内容*/
    public void Addnode(){
        System.out.println("向已经建立根结点的树添加其他结点内容");
        for (int i = this.roon+2; i < this.numnode; i++) {
            BoxNode b2=new BoxNode();
            nodes[i]=b2;
            System.out.println("输入第"+(i+1)+"结点元素");
            nodes[i].data=new Scanner(System.in).nextLine();
            System.out.println("输入第"+(i+1)+"结点双亲下标");
            Scanner scanner0=new Scanner(System.in);
            nodes[i].parent=scanner0.nextInt();
            while (nodes[i].parent>=i){
                System.out.println("parent下标错误,请再输入:");
                nodes[i].parent=scanner0.nextInt();
            }

            //当建立后续结点之后 就要考虑其兄弟信息
            //建立一个新的孩子结点
            ChildNode p=new ChildNode();
            p.child=i;
            p.next=null;

            int tem_p=nodes[i].parent;//保存双亲信息
            if(nodes[tem_p].firstchild==null){//如果此时构造的结点 的双亲的第一个孩子不存在,那么将孩子结点p赋予它
                //这个时候孩子结点p  保存了新生成结点的信息
                nodes[tem_p].firstchild=p;
            }else{//如果其本身有第一个孩子 ,那么就将新生成的结点 保存在其第一个孩子的后面,构成一个单链表
                ChildNode tem_c=nodes[tem_p].firstchild;
                while(tem_c.next!=null){
                    tem_c=tem_c.next;
                }
                tem_c.next=p;
            }
        }
    }
    //

    public static String toStringshuzu(Object[] a){
        if (a == null)
            return "null";

        int iMax = a.length - 1;
        if (iMax == -1)
            return "[]";

        StringBuilder b = new StringBuilder();
        //b.append(' ');
        for (int i = 0; ; i++) {
            b.append(String.valueOf(a[i]));
            if (i == iMax)
                return b.toString();
            //b.append("\t");
        }
    }
    @Override
    public String toString() {
        System.out.println("parent"+"\t"+"element");
        return toStringshuzu(nodes);
    }
}

无序二叉树的实现

数据结构——二叉树|二叉树的链式表示|前序输出|后续输出|中序输出|销毁等操作
单个单个加入

package com.treetest.test01;

public class BTreeTest01 {
    public static void main(String[] args) {
        TreeBinary t=new TreeBinary();
        t.add(5);
        t.add(2);;
        t.add(6);
        t.add(3);
    }
}

class Node{
    int data;
    Node Left;
    Node Right;

    public Node(int data) {
        this.data = data;
        Left = null;
        Right = null;
    }
}

class TreeBinary{
    Node root;//定义一个根结点,以此为开始

    public TreeBinary() {

    }

    //我的想法是 循环生成二叉树,而不是单个单个加
    //既然之前都行不通,那先尝试单个单个加的

    private Node addNode(Node currentnode,int value){
        if(currentnode==null)
            return new Node(value);

        if (value < currentnode.data) {
            currentnode.Left = addNode(currentnode.Left, value);
        } else if (value > currentnode.data) {
            currentnode.Right = addNode(currentnode.Right, value);
        } else {
            return currentnode;
        }
        return currentnode;
    }
    public void add(int v){
        root=addNode(root,v);
    }
}

循环遍历加入


import java.util.Scanner;

public class BTreeTest {
    public static void main(String[] args) {
        Btree tree=new Btree();
        tree.creatTree(tree.root);
        System.out.println("****前序输出树****");//这三种区分是按照根结点来的,先输出根 就是前序,先输左 根  右  就是中序输出
        tree.printbefore(tree.root);
        System.out.println();
        System.out.println("****中序输出树****");
        tree.printmid(tree.root);
        System.out.println();
        System.out.println("****后序输出树****");
        tree.printafter(tree.root);
    }
}
class BNode<E>{
    E data;
    BNode Lchild;
    BNode Rchild;

    public BNode(E data) {
        this.data = data;
        this.Lchild=null;
        this.Rchild=null;
    }

    public BNode() {

    }
}

class Btree{
    BNode root;

    public Btree() {

    }

    public BNode creatTree(BNode node){
        System.out.println("input a element:");
        Scanner scanner=new Scanner(System.in);
        String value=scanner.nextLine();
        if(value.equals("#")){//遇到# 结束此分支插入。
            return null;
        }
        if(root==null){
            node=new BNode<String>();
            node.data=value;
            root=node;
            //return root;
            System.out.println("插入根结点的左子树:");
            root.Lchild=creatTree(root.Lchild);//一开始总是连接不起来,最后原因是因为结点之间没有相连接,这和C++不一样,
            // C++有指针,所以实际上是指向的,而这里是引用,所以要我们自己加
            //creatTree(root.Lchild);
            System.out.println("插入根结点的柚子树:");
            root.Rchild=creatTree(root.Rchild);
            //creatTree(root.Rchild);
            return root;
        }else{
            node=new BNode();
            node.data=value;

            System.out.println("插入"+node.data+"结点的左子树:");
            node.Lchild=creatTree(node.Lchild);
            //creatTree(node.Lchild);
            System.out.println("插入"+node.data+"结点的柚子树:");
            node.Rchild=creatTree(node.Rchild);
            //creatTree(node.Rchild);
            return node;
        }
    }

    @Override
    public String toString() {
        return "Btree{" +
                "root=" + root +
                '}';
    }

    public int printbefore(BNode node){
        if(node==null)
            return 0;
        else
            System.out.print(node.data);
        if(node.Lchild!=null)
            printbefore(node.Lchild);
        if(node.Rchild!=null)
            printbefore(node.Rchild);
        return 0;
    }

    public int printmid(BNode node){
        if(node==null)
            return 0;
        if (node.Lchild!=null)
            printmid(node.Lchild);
        System.out.print(node.data);
        if(node.Rchild!=null)
            printmid(node.Rchild);
        return 0;
    }

    public int printafter(BNode node){
        if(node==null)
            return 0;
        if(node.Lchild!=null)
            printafter(node.Lchild);
        if(node.Rchild!=null){
            printafter(node.Rchild);
        }
        System.out.print(node.data);
        return  0;
    }
}

前序线索二叉树,中序线索二叉树

前序线索化和后续线索化都相对简单
和遍历二叉树类似
把握好

if(tree.lchild==null){
	tree.child=tree;
	tree.ltag=1;
}
if(pre!=null&&pre.rchild==null){
	pre.child=tree;
	pre.Rtag=1;
	}
	pre=p;

在进入左右子树的前后位置就好

稍难处在于遍历输出已经线索化的二叉树
遍历输出前序线索二叉树
思路:对于相对中间结点来说,先中间 后左右
所以第一个是根结点,后面是左边的相对根结点

所以在找到最左边的子树的时候,每次都对其相对根结点做处理

因为线索化,其左右孩子之间已经建立了联系
所以这时候我们根据线索标记 来寻找相对右结点的结点

而在最右边一个结点记得做null跳出处理。

public void Beoutputtree(CBNode tree){
        if (tree==null)
            return;
        else {

            while (tree.Ltag==0){//找到最边上一个结点
                System.out.println(tree.data);
                tree=tree.lchild;
            }
            //tree=H
            while (tree!=null){
                System.out.println(tree.data);
                if(tree.rchild==null)//最后一个结点右孩子为空时跳出
                    return;
                if (tree.Rtag==1){
                    tree=tree.rchild;
                }else {
                    tree=tree.lchild;
                }
            }

        }
    }

实现

package com.treetest.test01;
import java.util.Scanner;
public class clueBTree {
    public static void main(String[] args) {
        CBTree tree=new CBTree();
        tree.CreatBTree(tree);

        //前序线索
        tree.BeThread(tree.root);
        tree.Beoutputtree(tree.root);
        //中序线索
       tree.MidThread(tree.root);
       tree.Mioutputtree(tree.root);
        //后序线索
        tree.aftThread(tree.root);
    }
}
/*线索化二叉树  在进行遍历二叉树的时候,所得是一个单向链表,如果我们在遍历过程 按照遍历顺序将每个结点前后加上线索,即可以形成一个双向链表*/

class CBNode<E>{
    E data;
    CBNode rchild;
    CBNode lchild;
    int Ltag;
    int Rtag;

    public CBNode(E data) {
        this.data = data;
        this.rchild = null;
        this.lchild = null;

        this.Ltag=0;
        this.Rtag=0;
    }

    public CBNode(){

    }
}

class CBTree extends CBNode{
    CBNode root;

    public CBTree() {
    }

    public CBNode CreatBTree(CBNode node){
        System.out.println("输入一个结点信息:");
        Scanner scanner=new Scanner(System.in);
        String ele=scanner.nextLine();
        if(ele.equals("#")){
            return null;
        }
        if(root==null){
            node=new CBNode(ele);
            root=node;

            System.out.println("插入根结点的左子树:");
            root.lchild=CreatBTree(root.lchild);
            System.out.println("插入根结点的柚子树:");
            root.rchild=CreatBTree(root.rchild);

            return root;
        }else{
            node=new CBNode();
            node.rchild=null;
            node.lchild=null;
            node.Rtag=node.Ltag=0;//添加时都没有前驱后继信息

            node.data=ele;
            System.out.println("插入"+node.data+"结点的左子树:");
            node.lchild=CreatBTree(node.lchild);
            System.out.println("插入"+node.data+"结点的柚子树:");
            node.rchild=CreatBTree(node.rchild);

            return node;
        }
    }

    //前序线索化   将线索化 放在前面   进入子节点放后面,pre标记 一直跟在线索化后面 要不断更新pre
    private  CBNode pre;
    public void BeThread(CBNode p){
        if(p==null)
            return;
        else {
           if(p.lchild==null){
               p.Ltag=1;
               p.lchild=pre;
           }
           if(pre!=null&&pre.rchild==null){
               pre.rchild=p;
               pre.Rtag=1;
           }
           pre=p;

           if(p.Ltag==0){
               BeThread(p.lchild);
           }
           if(p.Rtag==0){
               BeThread(p.rchild);
           }

        }
    }
    /*遍历输出前序线索二叉树*/

    public void Beoutputtree(CBNode tree){
        if (tree==null)
            return;
        else {

            while (tree.Ltag==0){//找到最边上一个结点
                System.out.println(tree.data);
                tree=tree.lchild;
            }
            //tree=H
            while (tree!=null){
                System.out.println(tree.data);
                if(tree.rchild==null)//最后一个结点右孩子为空时跳出
                    return;
                if (tree.Rtag==1){
                    tree=tree.rchild;
                }else {
                    tree=tree.lchild;
                }
            }

        }
    }

    // 中序线索化   将进入左右孩子分支 放在首位  线索化放中间

    public void MidThread(CBNode p){
        if (p==null)
            return;
        else {

            MidThread(p.lchild);
            if (p.lchild==null){
                p.Ltag=1;
                p.lchild=pre;
            }
            if(pre!=null&&pre.rchild==null){
                pre.Rtag=1;
                pre.rchild=p;
            }
            pre=p;

            MidThread(p.rchild);
        }
    }

    public void Mioutputtree(CBNode tree){
        if (tree!=null){//先直接遍历到最左边的左子树
            while (tree.Ltag==0){
                tree=tree.lchild;
            }

            while (tree!=null){

                System.out.println(tree.data);

                if(tree.Rtag==1){//如果右子树 是线索,回到那
                    tree=tree.rchild;

                }else{//如果不是线索  那么我们没有必要再走一次 不然进入两个结点的死循环
                    tree=tree.rchild;
                    if(tree==null)
                        return;
                    while (tree.Ltag==0&&tree!=null){
                        tree=tree.lchild;
                    }

                }
            }


        }
    }

    public void aftThread(CBNode p){
        if(p==null){
            return;
        }else {

            if (p.Ltag==0){
                aftThread(p.lchild);
            }
            if(p.Rtag==0){
                aftThread(p.rchild);
            }


            if(p.lchild==null){
                p.lchild=pre;
                p.Ltag=1;
            }
            if(pre!=null&&pre.rchild==null){
                pre.rchild=p;
                pre.Rtag=1;
            }
            pre=p;
        }
    }


    //后续线索化输出   比较难 单独讲

}

后续线索二叉树

遍历后续线索二叉树,可以看出,只有每条支路的最左边才和左结点有联系

其他的都是和其相对根节点建立起了联系
因此要建立双亲指针,来寻找左右兄弟

class CBNode<E>{
    E data;
    CBNode rchild;
    CBNode lchild;
    int Ltag;
    int Rtag;
    CBNode parent;
    public CBNode(E data) {
        this.data = data;
        this.rchild = null;
        this.lchild = null;

        this.parent=null;
        this.Ltag=0;
        this.Rtag=0;
    }

    public CBNode(){

    }
}

建立二叉树时建立双亲指向

class CBTree extends CBNode{
    CBNode root;

    public CBTree() {
    }

    public CBNode CreatBTree(CBNode node){
        System.out.println("输入一个结点信息:");
        Scanner scanner=new Scanner(System.in);
        String ele=scanner.nextLine();
        if(ele.equals("#")){
            return null;
        }
        if(root==null){
            node=new CBNode(ele);
            root=node;

            System.out.println("插入根结点的左子树:");
            root.lchild=CreatBTree(root.lchild);
            if(root.lchild!=null)
                root.lchild.parent=root;
            System.out.println("插入根结点的柚子树:");
            root.rchild=CreatBTree(root.rchild);
            if (root.rchild!=null)
                root.rchild.parent=root;
            return root;
        }else{
            node=new CBNode();

            node.data=ele;

            System.out.println("插入"+node.data+"结点的左子树:");
            node.lchild=CreatBTree(node.lchild);
            System.out.println("插入"+node.data+"结点的柚子树:");
            node.rchild=CreatBTree(node.rchild);

            if (node.lchild!=null)
                node.lchild.parent=node;

            if (node.rchild!=null)
                node.rchild.parent=node;

            return node;
        }
    }

后序线索化+遍历后续二叉树

public void aftThread(CBNode p){
        if(p==null){
            return;
        }else {

            if (p.Ltag==0){
                aftThread(p.lchild);
            }
            if(p.Rtag==0){
                aftThread(p.rchild);
            }


            if(p.lchild==null){
                p.lchild=pre;
                p.Ltag=1;
            }
            if(pre!=null&&pre.rchild==null){
                pre.rchild=p;
                pre.Rtag=1;
            }
            pre=p;
        }
    }


    //后续线索化输出
    //后续线索二叉树中,存在 相邻的左右结点 没有直接联系
    //通过回溯父结点  来处理
    public void aftOutputree(CBNode tree){
        if (tree==null){
            return;
        }
        while (tree!=null&&tree.Ltag==0){//先到最左端
            tree=tree.lchild;
        }

        CBNode p=null;//保留遍历过程中上一个结点信息

        while (tree!=null){
            if(tree.Rtag==1){
                System.out.println(tree.data);
                p=tree;
                tree=tree.rchild;
            }else {
                if (tree.rchild==p){//说明往右走会重复循环——需要往双亲走

                    System.out.println(tree.data);
                                    //可能遇到根结点 后续到根节点就返回
                    if (tree==root)
                        return;

                    p=tree;
                    tree=tree.parent;
                }else {//进入双亲结点 且进入右分支 寻找最左结点
                    tree=tree.rchild;
                    while (tree!=null&&tree.Ltag==0){
                        tree=tree.lchild;
                    }
                }
            }
        }

    }

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值