上篇博客对链表的结构进行了代码实现,今天让我用java代码带大家了解二叉树的实现原理。
首先大家对二叉树的结构都应该了解,二叉树是由节点构成的,每个节点最多有两个字节点,称为左子节点和右子节点,还有parent父节点。
今天的demo注释写的非常清楚,希望有兴趣的可以跟着注释来看,如果有不懂 的可以在下方评论,我会及时回复。

节点的定义

class Node{
        private T date;//节点数据
        private Node parent;//父节点
        private Node left;//左子节点
        private Node right;//右子节点

        public Node(T date){
            this.date = date;
        }
    }

二叉树的属性

private Node root;//根节点

数据的添加

/**
     * 添加数据
     * @param date
     * @return
     */
    public boolean add(T date){
        boolean flag = false;
        //判断数据是否已经存在
        if(!exist(date)){//数据不存在,可以添加
            Node newNode = new Node(date);//构建新节点
            //判断是否有数据
            if(root==null){
                root = newNode;//将新建节点作为根节点
            }else{
                //找到父节点
                Node parent = findParent(date);
                if(parent!=null){
                    if(parent.date.compareTo(date)>0){
                        parent.left =  newNode;//父节点的左节点指向新建节点
                    }else{
                        parent.right = newNode;//父节点的右节点指向新建节点
                    }
                    newNode.parent = parent;//新建节点的parent节点指向父节点
                }
            }
            flag = true;

        }else{
            return flag;
        }
        return flag;
    }

删除某个数据

该部分逻辑比较复杂,但我每一种情况都分析到了,按主次情况已经一一列出。

/**
     * 删除数据
     * @param date
     * @return
     */
    public boolean remove(T date){
        boolean flag = false;
        Node node = getNode(date);
        if(node!=null){
            if(node==root){//1.删除的是根节点
                if(node.right==null && node.left==null){//1.1无左右子节点
                    root = null;
                }
                else if(node.left==null && node.right!=null){//1.2无左节点
                    node.right.parent=null;//将要删节点的右侧节点的parent置为null
                    root = node.right;//将要删节点的右节点作为根节点
                }else if(node.left!=null && node.right==null){//1.3无右节点
                    node.left.parent = null;//将要删节点的左侧节点的parent置为null
                    root = node.left;//将要删节点的左节点作为根节点
                }else{//1.4有左右节点
                    Node left = split(date);//分割节点,得到要删节点的左节点,
                    root = left;//将left节点作为根节点
                    left.parent = null;//将left的parent置为null
                }
            }else{//2.删除的是非根节点
                if(node.right==null && node.left==null){//2.1无左右子节点
                    if(node.date.compareTo(node.parent.date)<0){//该节点是父节点的左子节点
                        node.parent.left = null;//将父节点的left置为null
                    }else{//该节点的父节点的右字节点
                        node.parent.right = null;//将父节点的right置为null
                    }
                }else if(node.left==null && node.right!=null){//2.2无左节点
                    node.right.parent = node.parent;//将该节点的右节点的parent指向该节点的parent
                    node.parent.right = node.right;//将该节点的parent节点的right指向该节点的right节点
                }else if(node.left!=null && node.right==null){//2.3无右节点
                    node.parent.right = node.left;//将该节点的父节点的right指向该节点的left节点
                    node.left.parent = node.parent;//将该节点得left节点的parent指向该节点的parent节点
                }else{//2.4有左右节点
                    Node left = split(date);//将要删的节点分割,返回该节点的左节点
                    node.parent.left = node.left;//将要删节点的父节点的left指向该节点的左节点,
                    left.parent = node.parent;//将分割后的左节点的parent指向要删节点的父节点
                }
            }
            flag = true;
        }
        return flag;
    }

遍历

public void print() {
        see(root);
    }
    /**
     * 打印节点数据
     * @param root2
     */
    private void see(Node node) {//中序打印
        if(node!=null){
            see(node.left);
            System.out.println(node.date);
            see(node.right);
        }
    }

用到的几个辅助方法

  • split(T date)—>
    将从指定节点切割,并将被切掉节点的右子节点拼接在左节点正确的位置,将剩余的漏出来的左节点返回。
/**
     * 分割要删数据的节点,默认左节点作为父节点,并将右节点指向左节点的最大节点的right
     * @param date
     * @return
     */
    private Node split(T date) {
        //找到节点
        Node node = getNode(date);
        //找到左节点的最大数节点
        Node  big = getBig(node.left);
        //将要分割节点的右节点的parent指向big
        node.right.parent = big;
        //将big的右节点指向node的右节点
        big.right = node.right;
        return node.left;
    }
  • getBig(Node node)—>
    从指定节点向下寻找,找到数据值最大的节点并返回
/**
     * 从node开始找最大的节点
     * @param left
     * @return
     */
    private Node getBig(Node node) {
        Node temp = node;
        while(temp!=null){
            if(temp.right!=null){
                temp = temp.right;
            }else{
                return temp;
            }
        }
        return null;
    }
  • findParent(T date)—–>
    找到新建节点即将插入树中的它的父节点。
/**
     * 找到父节点
     * @param date
     * @return
     */
    private Node findParent(T date) {
        Node temp = root;//从根节点开始遍历
        Node prev = temp;
        while(temp!=null){//跳出循环时,temp为null,prev记录上一个节点
            prev = temp;//用prev记录上一个节点
            if(temp.date.compareTo(date)<0){//从右节点寻找
                temp = temp.right;
            }else{//从左节点开始遍历
                temp = temp.left;
            }
        }
        return prev;
    }
  • exist(T date)—>
    判断树中是否存在该数据
/**
     * 判断数据存在与否
     * @param date
     * @return
     */
    private boolean exist(T date) {
        boolean flag = false;
        Node temp = root;//从根节点开始遍历
        while(temp!=null){
            if(temp.date.compareTo(date)==0){//数据已经存在
                flag = true;
                break;
            }else{
                if(temp.date.compareTo(date)<0){//从右节点寻找
                    temp = temp.right;
                }else{//从左节点开始遍历
                    temp = temp.left;
                }
            }
        }
        return flag;
    }
  • getNode(T date)—>
    在树中找到某个数据对应的节点
/**
     * 获得节点
     * @param date
     * @return
     */
    private Node getNode(T date) {
        Node temp = root;//从根节点开始遍历
        while(temp!=null){
            if(temp.date.compareTo(date)==0){//数据已经存在
                return temp;
            }else{
                if(temp.date.compareTo(date)<0){//从右节点寻找
                    temp = temp.right;
                }else{//从左节点开始遍历
                    temp = temp.left;
                }
            }
        }
        return null;
    }

因为我不知道如何上传非图片的文件,所以只能把代码帖在最下面了。

package com.it;
/**
 * 
 * 该类实现有序二叉树的数据存储结构
 * @author Administrator
 *
 */
public class BinaryTree<T extends Comparable<T>> {
    class Node{
        private T date;//节点数据
        private Node parent;//父节点
        private Node left;//左子节点
        private Node right;//右子节点

        public Node(T date){
            this.date = date;
        }
    }
    private Node root;//根节点
    /**
     * 添加数据
     * @param date
     * @return
     */
    public boolean add(T date){
        boolean flag = false;
        //判断数据是否已经存在
        if(!exist(date)){//数据不存在,可以添加
            Node newNode = new Node(date);//构建新节点
            //判断是否有数据
            if(root==null){
                root = newNode;//将新建节点作为根节点
            }else{
                //找到父节点
                Node parent = findParent(date);
                if(parent!=null){
                    if(parent.date.compareTo(date)>0){
                        parent.left =  newNode;//父节点的左节点指向新建节点
                    }else{
                        parent.right = newNode;//父节点的右节点指向新建节点
                    }
                    newNode.parent = parent;//新建节点的parent节点指向父节点
                }
            }
            flag = true;

        }else{
            return flag;
        }
        return flag;
    }
    /**
     * 删除数据
     * @param date
     * @return
     */
    public boolean remove(T date){
        boolean flag = false;
        Node node = getNode(date);
        if(node!=null){
            if(node==root){//1.删除的是根节点
                if(node.right==null && node.left==null){//1.1无左右子节点
                    root = null;
                }
                else if(node.left==null && node.right!=null){//1.2无左节点
                    node.right.parent=null;//将要删节点的右侧节点的parent置为null
                    root = node.right;//将要删节点的右节点作为根节点
                }else if(node.left!=null && node.right==null){//1.3无右节点
                    node.left.parent = null;//将要删节点的左侧节点的parent置为null
                    root = node.left;//将要删节点的左节点作为根节点
                }else{//1.4有左右节点
                    Node left = split(date);//分割节点,得到要删节点的左节点,
                    root = left;//将left节点作为根节点
                    left.parent = null;//将left的parent置为null
                }
            }else{//2.删除的是非根节点
                if(node.right==null && node.left==null){//2.1无左右子节点
                    if(node.date.compareTo(node.parent.date)<0){//该节点是父节点的左子节点
                        node.parent.left = null;//将父节点的left置为null
                    }else{//该节点的父节点的右字节点
                        node.parent.right = null;//将父节点的right置为null
                    }
                }else if(node.left==null && node.right!=null){//2.2无左节点
                    node.right.parent = node.parent;//将该节点的右节点的parent指向该节点的parent
                    node.parent.right = node.right;//将该节点的parent节点的right指向该节点的right节点
                }else if(node.left!=null && node.right==null){//2.3无右节点
                    node.parent.right = node.left;//将该节点的父节点的right指向该节点的left节点
                    node.left.parent = node.parent;//将该节点得left节点的parent指向该节点的parent节点
                }else{//2.4有左右节点
                    Node left = split(date);//将要删的节点分割,返回该节点的左节点
                    node.parent.left = node.left;//将要删节点的父节点的left指向该节点的左节点,
                    left.parent = node.parent;//将分割后的左节点的parent指向要删节点的父节点
                }
            }
            flag = true;
        }
        return flag;

    }
    /**
     * 分割要删数据的节点,默认左节点作为父节点,并将右节点指向左节点的最大节点的right
     * @param date
     * @return
     */
    private Node split(T date) {
        //找到节点
        Node node = getNode(date);
        //找到左节点的最大数节点
        Node  big = getBig(node.left);
        //将要分割节点的右节点的parent指向big
        node.right.parent = big;
        //将big的右节点指向node的右节点
        big.right = node.right;
        return node.left;
    }
    /**
     * 从node开始找最大的节点
     * @param left
     * @return
     */
    private Node getBig(Node node) {
        Node temp = node;
        while(temp!=null){
            if(temp.right!=null){
                temp = temp.right;
            }else{
                return temp;
            }
        }
        return null;
    }
    /**
     * 找到父节点
     * @param date
     * @return
     */
    private Node findParent(T date) {
        Node temp = root;//从根节点开始遍历
        Node prev = temp;
        while(temp!=null){//跳出循环时,temp为null,prev记录上一个节点
            prev = temp;//用prev记录上一个节点
            if(temp.date.compareTo(date)<0){//从右节点寻找
                temp = temp.right;
            }else{//从左节点开始遍历
                temp = temp.left;
            }
        }
        return prev;
    }
    /**
     * 判断数据存在与否
     * @param date
     * @return
     */
    private boolean exist(T date) {
        boolean flag = false;
        Node temp = root;//从根节点开始遍历
        while(temp!=null){
            if(temp.date.compareTo(date)==0){//数据已经存在
                flag = true;
                break;
            }else{
                if(temp.date.compareTo(date)<0){//从右节点寻找
                    temp = temp.right;
                }else{//从左节点开始遍历
                    temp = temp.left;
                }
            }
        }
        return flag;
    }
    /**
     * 获得节点
     * @param date
     * @return
     */
    private Node getNode(T date) {
        Node temp = root;//从根节点开始遍历
        while(temp!=null){
            if(temp.date.compareTo(date)==0){//数据已经存在
                return temp;
            }else{
                if(temp.date.compareTo(date)<0){//从右节点寻找
                    temp = temp.right;
                }else{//从左节点开始遍历
                    temp = temp.left;
                }
            }
        }
        return null;
    }

    public void print() {
        see(root);
    }
    /**
     * 打印节点数据
     * @param root2
     */
    private void see(Node node) {//中序打印
        if(node!=null){
            see(node.left);
            System.out.println(node.date);
            see(node.right);
        }
    }

}