Java 数据结构_树

Java 数据结构_树


本文由 Luzhuo 编写,转发请保留该信息.
原文: http://blog.csdn.net/Rozol/article/details/78469973


树的基本概念

  • 树是结点的有限集合
  • 双亲: 根结点A是BCD的双亲, 下面的BCD都是A的孩子
  • 兄弟: 同一个双亲之间的结点, 互称为兄弟, BCD互为兄弟
  • 堂兄弟: 双亲在同一层的结点, 互称为堂兄弟, EFGHIJKL互为堂兄弟
  • 度: 连接他的结点数, A的度为3
  • 根结点: 无双亲, 且唯一的结点
  • 分支结点(非终端结点): 度不为0的结点, BCD都是分支结点, 也称中间结点(除A外)
  • 叶结点(终端结点): 度为0的结点, EFGHIJKL都是叶结点.
  • 祖先: 之前所有的结点都是祖先, G的祖先是BA
  • 子孙: 之后所有的结点都是子孙, B的子孙是EFG
  • 有序树(从左到右不能换顺序), 无序树(可换顺序)
  • 深度: 树的最大深度, 这颗树的深度为3
  • 森林: 多个独立的树(互不相交)放在一起就是森林
  • 二叉树: 每个结点的度最多为2的树
  • 树的遍历:

  • 用途:压缩软件-赫夫曼树 / 搜索-人机对战

二叉树的实现方式

数组实现方式, 删除指定索引位置的结点时, 不会删除他的所有孩子
链式实现方式, 删除指定结点时, 会删除他的所有孩子

二叉树的数组实现方式

二叉树的数组实现代码

/**
 * 二叉树(数组的表示方式)
 * @author Luzhuo
 *         A(0)
 *    B(1)       C(2)
 *  D(3) E(4)  F(5) G(6)    
 * 
 * |A(0)| B(1)|C(2) | D(3)|E(4) | F(5)|G(6) | ...|... |
 */
public class ArrayTree {
    // 树的数组表示方式
    private Node[] nodes; // 树的指针
    private int size; // 数组的大小

    public ArrayTree(Node root){
        init(1024, root);
    }

    public ArrayTree(int size, Node root){
        init(size, root);
    }

    /**
     * 创建二叉树<br>
     * 创建存放结点的数组, 设置该数据结构的容量, 设置根结点
     * @param size
     * @param root
     */
    private void init(int size, Node root){
        this.nodes = new Node[size];
        this.size = size;
        // 根结点
        this.nodes[0] = root;
    }

    /**
     * 销毁二叉树<br>
     * 删除存放结点的数组
     */
    public void destory(){
        nodes = null;
    }

    /**
     * 根据索引搜索结点
     * @param nodeIndex 索引
     * @return 结点
     */
    public Node serchNode(int nodeIndex){
        if(nodeIndex < 0 || nodeIndex >= this.size){
            throw new ArrayIndexOutOfBoundsException("索引越界.");
        }

        return nodes[nodeIndex];
    }

    /**
     * 添加结点<br>
     * 添加在父结点的左边时, 添加到数组的奇数索引位置(1, 3, 5 ...), 添加在父结点的右边时, 添加到数组的偶数索引位置(2, 4, 6 ...)
     * @param nodeIndex 结点索引(从0开始)
     * @param direction 左边0还是右边1
     * @param node 结点
     * @return 是否添加成功
     */
    public boolean addNode(int nodeIndex, int direction, Node node){
        if(nodeIndex < 0 || nodeIndex >= this.size){
            throw new ArrayIndexOutOfBoundsException("索引越界.");
        }

        if(this.nodes[nodeIndex] == null){
            throw new NullPointerException("该结点为空");
        }

        // 若某结点再父节点下标 * 2 + 1 ,则表示该结点在左
        // 若某结点再父节点下标 * 2 + 2, 则表示该结点在右
        int leftIndex = nodeIndex * 2 + 1;
        int rightIndex = nodeIndex * 2 + 2;
        if(direction == 0){
            if(leftIndex >= this.size || this.nodes[leftIndex] != null) return false;
            this.nodes[leftIndex] = node;
        }else if(direction == 1){
            if(rightIndex >= this.size || this.nodes[rightIndex] != null) return false;
            this.nodes[rightIndex] = node;
        }
        return true;
    }

    /**
     * 删除索引<br>
     * @param nodeIndex
     * @return 被删除的结点
     */
    public Node deleteNode(int nodeIndex){
        if(nodeIndex < 0 || nodeIndex >= this.size){
            throw new ArrayIndexOutOfBoundsException("索引越界.");
        }

        if(this.nodes[nodeIndex] == null){
            throw new NullPointerException("该结点为空");
        }

        Node tempN = this.nodes[nodeIndex];
        this.nodes[nodeIndex] = null;
        return tempN;
    }

    /**
     * 遍历<br>
     * 之间从左向右遍历数组.
     */
    public void traverse(){
        for(int i = 0; i < this.size; i++){
            Node data = this.nodes[i];
            if(data != null) System.out.print(data + " ");
        }
    }

}

class Node{
    public char data;

    public Node(char data){
        this.data = data;
    }

    @Override
    public String toString() {
        return String.valueOf(data);
    }
}

数组方式测试代码

public class ArrayTreeTest {

    public static void main(String[] args) {
        // 测试
        ArrayTree tree = new ArrayTree(new Node('A'));

//      tree.addNode(0, 0, new Node('B'));
//      tree.addNode(0, 1, new Node('C'));
//      tree.addNode(1, 0, new Node('D'));
//      tree.addNode(1, 1, new Node('E'));
//      tree.addNode(2, 0, new Node('F'));
//      tree.addNode(2, 1, new Node('G'));
        // ↓ 简化

        int nodeIndex = -1;
        for(int i = 0; i < 10; i++){
            int direction;
            if(i%2 == 0){
                direction = 0;
                nodeIndex++;
            }else{
                direction = 1;
            }
            tree.addNode(nodeIndex, direction, new Node((char)(66+i)));
        }

        tree.traverse();

        System.out.println();

        System.out.println(tree.serchNode(3));
        System.out.println(tree.deleteNode(3));

        tree.traverse();

        tree.destory();
    }
}

二叉树的链式实现方式

二叉树的链式实现代码

/**
 * 二叉树(链式实现方式)
 * @author Luzhuo
 *         (0)
 *    A(1)       B(2)
 *  C(3) D(4)  E(5) F(6)  
 * 前序遍历(根左右): 0 1 3 4 2 5 6 
 * 中序遍历(左根右): 3 1 4 0 5 2 6
 * 后序遍历(左右根): 3 4 1 5 6 2 0 
 */
public class LinkedTree {
    Node_ root; // 根结点

    public LinkedTree(){
        init();
    }

    /**
     * 创建链式二叉树<br>
     * 初始化根结点的数据0, 索引0, 左孩子指针null, 右孩子指针null, 父节点指针null
     */
    private void init(){
        root = new Node_();
        root.data = 0;
        root.index = 0;
        root.lChildNode = null;
        root.rChildNode = null;
        root.parentNode = null;
    }

    /**
     * 销毁链式二叉树<br>
     * 递归删除所有孩子, 并指针重置为null
     */
    public void destory(){
        this.root.deleteNode();
        this.root = null;
    }

    /**
     * 根据索引寻找结点<br>
     * 递归查找指定索引位置的结点
     * @param nodeindex 索引
     * @return 结点
     */
    public Node_ searchNode(int nodeindex){
        return this.root.searchNode(nodeindex);
    }

    /**
     * 添加结点<br>
     * 找到指定位置的结点, 将新结点添加到该结点的左边 / 右边, 并将该结点的指针交给新结点
     * @param nodeindex 索引
     * @param direction 左边0或右边1
     * @param node 被挂在的结点
     * @return 添加成功返回true,否则返回false;
     */
    public boolean addNode(int nodeindex, int direction, Node_ node){
        Node_ curN = searchNode(nodeindex);
        if(curN == null) return false;

        // 加入到该结点的左边or右边
        if(direction == 0){
            curN.lChildNode = node;
        }else if(direction == 1){
            curN.rChildNode = node;
        }
        // 并该结点的指针交给插入的结点
        node.parentNode = curN;
        return true;
    }

    /**
     * 删除指定索引的结点, 注意删除结点,会将挂在在该结点的所有结点同时删除<br>
     * 找到指定的结点, 递归删除自己所有的孩子
     * @param nodeindex 索引
     * @return 被删除的结点, 该结点为空返回null
     */
    public Node_ deleteNode(int nodeindex){
        Node_ curN = searchNode(nodeindex);
        if(curN == null) return null;

        Node_ tempN = curN;
        curN.deleteNode();
        curN = null;
        return tempN;
    }

    /**
     * 前序遍历(根左右, 根前)
     */
    public void preorderTraversal(){
        this.root.preorderTraversal();
    }

    /**
     * 中序遍历(左根右, 根中)
     */
    public void inorderTraversal(){
        this.root.inorderTraversal();
    }

    /**
     * 后序遍历(左右根, 根后)
     */
    public void postorderTraversal(){
        this.root.postorderTraversal();
    }

}

/**
 * 结点, 创建必须给index和data赋值
 * @author Luzhuo
 *
 */
class Node_{
    public int index; // 索引
    public char data; // 数据
    public Node_ lChildNode; // 左结点指针
    public Node_ rChildNode; // 右结点指针
    public Node_ parentNode; // 父结点指针

    public Node_(){ }

    public Node_(int index, char data){
        this.index = index;
        this.data = data;
    }

    /**
     * 根据索引寻找结点
     * @param nodeindex 索引
     * @return 找到结点返回结点, 否则返回null
     */
    public Node_ searchNode(int nodeindex){
        // 是否是自身
        if(this.index == nodeindex) return this;

        Node_ tempN;
        // 是否是左边的孩子,及其子结点
        if(this.lChildNode != null){
            if(this.lChildNode.index == nodeindex){
                return this.lChildNode;
            }else{
                tempN = this.lChildNode.searchNode(nodeindex);
                if(tempN != null) return tempN;
            }
        }

        // 是否是右边的孩子,及其子结点
        if(this.rChildNode != null){
            if(this.rChildNode.index == nodeindex){
                return this.rChildNode;
            }else{
                tempN = this.rChildNode.searchNode(nodeindex);
                if(tempN != null) return tempN;
            }
        }
        return null;
    }

    public void deleteNode(){
        // 删除自己左右边的所有子结点
        if(this.lChildNode != null){
            this.lChildNode.deleteNode();
        }
        if(this.rChildNode != null){
            this.rChildNode.deleteNode();
        }
        // 删除自己在父结点上的指针
        if(this.parentNode != null){
            if(this.parentNode.lChildNode == this){
                this.parentNode.lChildNode = null;
            }
            if(this.parentNode.rChildNode == this){
                this.parentNode.rChildNode = null;
            }
        }       
    }

    /**
     * 前序遍历(根左右)<br>
     * 每个结点先输出自身(根), 然后递归左结点, 在递归右结点
     */
    public void preorderTraversal(){
        // 根
        System.out.println("index: " + this.index + " data: " + data + " === ");
        // 左
        if(this.lChildNode != null) this.lChildNode.preorderTraversal();
        // 右
        if(this.rChildNode != null) this.rChildNode.preorderTraversal();
    }

    /**
     * 中序遍历(左根右)
     */
    public void inorderTraversal(){
        // 左
        if(this.lChildNode != null) this.lChildNode.inorderTraversal();
        // 根
        System.out.println("index: " + this.index + " data: " + data + " === ");
        // 右
        if(this.rChildNode != null) this.rChildNode.inorderTraversal();
    }

    /**
     * 后序遍历(左右根)
     */
    public void postorderTraversal(){
        // 左
        if(this.lChildNode != null) this.lChildNode.postorderTraversal();
        // 右
        if(this.rChildNode != null) this.rChildNode.postorderTraversal();
        // 根
        System.out.println("index: " + this.index + " data: " + data + " === ");

    }
}

数组方式测试代码

public class LinkedTreeTest {
    public static void main(String[] args) {
        // 测试
        /*
         *         (0)
         *    A(1)       B(2)
         *  C(3) D(4)  E(5) F(6)  
         * 前序遍历(根左右): 0 1 3 4 2 5 6 
         * 中序遍历(左根右): 3 1 4 0 5 2 6
         * 后序遍历(左右根): 3 4 1 5 6 2 0 
         */

        LinkedTree tree = new LinkedTree();

//      tree.addNode(0, 0, new Node_(1, 'A'));
//      tree.addNode(0, 1, new Node_(2, 'B'));
//      tree.addNode(1, 0, new Node_(3, 'C'));
//      tree.addNode(1, 1, new Node_(4, 'D'));
//      tree.addNode(2, 0, new Node_(5, 'E'));
//      tree.addNode(2, 1, new Node_(6, 'F'));
        // ↓ 简化

        int nodeIndex = -1;
        for(int i = 0; i < 10; i++){
            int direction;
            if(i%2 == 0){
                direction = 0;
                nodeIndex++;
            }else{
                direction = 1;
            }
            tree.addNode(nodeIndex, direction, new Node_(i + 1, (char)(65+i)));
        }

        tree.preorderTraversal();
        System.out.println();
        tree.inorderTraversal();
        System.out.println();
        tree.postorderTraversal();

        System.out.println();
        System.out.println(tree.deleteNode(3).data);
        System.out.println(tree.deleteNode(2).data);
        tree.preorderTraversal();


        tree.destory();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值