基于链表的二叉搜索树实现(java)——树结构学习(1)

在链表实现的二叉搜索树中,对于每一个Node需满足:左子Node < Node <= 右子Node,最好情况下二叉搜索的高度为\log_{2}N,此时查找、增加、删除的平均时间复杂度为O(\log_{2}N),最差情况下即按照某种顺序插入Node构建的树高度为N,此时增删查的平均时间复杂度为O(N)。代码分为Node类,二叉树抽象类,二叉搜索树实现类。 

目录

BinaryNode类(简称Node)

BinaryTree二叉树抽象类(便于后面扩展)

中序遍历

BinarySearchTree(继承自BinaryTree)

查找

插入

最大/小值

删除


BinaryNode类(简称Node)

Node含有的泛型数据data必须实现Comparable接

口,用来实现Node内部数据的比较,Node本身也实现Comparable接口,两个Node比较时直接调用各自data的compareTo方法。Node也需要包含对父Node以及左右子Node的引用,代码如下:

package TreeStructure;

import org.jetbrains.annotations.NotNull;

public class BinaryNode<T extends Comparable> implements Comparable<BinaryNode<T>> {
    private T data;
    private BinaryNode<T> parent;
    private BinaryNode<T> leftChild;
    private BinaryNode<T> rightChild;

    public BinaryNode(T data) {
        this.data = data;
    }

    public BinaryNode(T data, BinaryNode<T> parent) {
        this.data = data;
        this.parent = parent;
    }

    @Override
    public int compareTo(@NotNull BinaryNode<T> tBinaryNode) {
        return this.data.compareTo(tBinaryNode.getData());
    }

    public void visit(){
        //节点的访问动作写在这里
        System.out.print(data + " ");
    }

    public boolean hasChild(){
        if(this.getLeftChild() == null && this.getRightChild() == null) return false;
        else return true;
    }

    public boolean hasLeftChild(){
        if(this.getLeftChild() == null) return false;
        return true;
    }

    public boolean hasRightChild(){
        if(this.getRightChild() == null) return false;
        return true;
    }

    public boolean isLeftChildOf(BinaryNode<T> parentBinaryNode){
        if(parentBinaryNode != null && parentBinaryNode.getLeftChild() == this)return true;
        return false;
    }

    public boolean isRightChildOf(BinaryNode<T> parentBinaryNode){
        if(parentBinaryNode != null && parentBinaryNode.getRightChild() == this)return true;
        return false;
    }

    public BinaryNode<T> getParent() {
        return parent;
    }

    //返回Node右子树中的最小值
    public BinaryNode<T> getSuccessor(){
        if(rightChild == null) return null;
        BinaryNode<T> current = rightChild;
        while(current.hasLeftChild())current = current.getLeftChild();
        return current;
    }

    public void setParent(BinaryNode<T> parent) {
        this.parent = parent;
    }


    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public BinaryNode<T> getLeftChild() {
        return leftChild;
    }

    public void setLeftChild(BinaryNode<T> leftChild) {
        this.leftChild = leftChild;
    }

    public BinaryNode<T> getRightChild() {
        return rightChild;
    }

    public void setRightChild(BinaryNode<T> rightChild) {
        this.rightChild = rightChild;
    }
}

BinaryTree二叉树抽象类(便于后面扩展)

包含一个根Node的引用(root),方法包含增删查以及前中后序遍历,由于二叉树的前中后序遍历方法一样,因此可以直接在该抽象类中实现:

中序遍历

对于一棵以Node为根Node的二叉树,先访问其左子树,再访问root,最后访问右子树,子树的处理方法与树一致,当子树的根Node为Null说明这棵子树是空的,可作为访问完毕的结束条件,从而递归遍历整棵树。

package TreeStructure;

public abstract class BinaryTree<T extends Comparable> {
    protected BinaryNode<T> root;

    public abstract BinaryNode<T> find(T key);

    public abstract void insert(T key);

    public abstract void delete(T key);

    //从跟Node开始中序遍历
    public void inOrder() {
        if (root != null) inOrder(root);
    }
    
    //从某个Node开始中序遍历
    public void inOrder(BinaryNode<T> binaryNode) {

        if (binaryNode == null) return;
        BinaryNode<T> leftChild = binaryNode.getLeftChild();
        BinaryNode<T> rightChild = binaryNode.getRightChild();
        if (leftChild != null) inOrder(leftChild);
        binaryNode.visit();
        if (rightChild != null) inOrder(rightChild);
    }
    
    //前序
    public void preOrder() {
        if (root != null) inOrder(root);
    }

    public void preOrder(BinaryNode<T> binaryNode) {
        if (binaryNode == null) return;
        BinaryNode<T> leftChild = binaryNode.getLeftChild();
        BinaryNode<T> rightChild = binaryNode.getRightChild();
        binaryNode.visit();
        if (leftChild != null) preOrder(leftChild);
        if (rightChild != null) preOrder(rightChild);
    }
    
    //后序
    public void postOrder() {
        if (root != null) inOrder(root);
    }

    public void postOrder(BinaryNode<T> binaryNode) {
        if (binaryNode == null) return;
        BinaryNode<T> leftChild = binaryNode.getLeftChild();
        BinaryNode<T> rightChild = binaryNode.getRightChild();
        if (leftChild != null) postOrder(leftChild);
        if (rightChild != null) postOrder(rightChild);
        binaryNode.visit();
    }
}

BinarySearchTree(继承自BinaryTree)

查找

假设查找key,若根Node等于key则找到,若根Node比key大则在左子树中找,若根Node大于或等于key则在右子树中找,如果子树为null说明树中不存在key。

插入

插入方法与查找类似,key小于该Node就在左子树中找位置,key大于等于该Node就在右子树中找位置,直到Node的子树为null,该位置即插入位置。

最大/小值

从root开始,按照左子树的方向找下去最后一个Node为最小值,最大值同理。

删除

删除方法较复杂,找到待删除Node后分三种情况:

  1. 待删除Node不含子Node,则将该Node直接删除。
  2. 待删除Node含有一个子Node,则将该Node删除后,再把它的父Node与该子Node相连
  3. 待删除Node含有两个子Node,则在待删除的Node的右子树中找最小值作为Successor,Successor的值覆盖待删除Node后,删除Successor,Successor不可能含有左子树,如果含有左子树,说明还有比Successor小的值,因此删除Successor时用情况1或2的方法即可。
package TreeStructure;

public class BinarySearchTree<T extends Comparable<T>> extends BinaryTree<T> {


    @Override
    public BinaryNode<T> find(T key) {
        if (root == null) return null;
        BinaryNode<T> current = root;
        while (current.getData().compareTo(key) != 0) {
            if (current.getData().compareTo(key) > 0) current = current.getLeftChild();
            else current = current.getRightChild();
            if (current == null) return null;
        }
        return current;
    }

    @Override
    public void insert(T key) {
        if (root == null)
            //树上没有Node直接把root设置为插入Node
            root = new BinaryNode<>(key);
        else {
            //插入Node的父Node
            BinaryNode<T> parent;
            //遍历Node
            BinaryNode<T> current = root;
            do {
                parent = current;
                if (current.getData().compareTo(key) > 0) current = current.getLeftChild();
                    //相等的Node插入右子树
                else current = current.getRightChild();
            } while (current != null);
            //此时Parent的子Node是null,null用插入Node替代
            if (parent.getData().compareTo(key) > 0) parent.setLeftChild(new BinaryNode<>(key, parent));
            else parent.setRightChild(new BinaryNode<>(key, parent));
        }
    }

    @Override
    public void delete(T key) {
        //待删除Node
        BinaryNode<T> delete = find(key);
        if (delete == null) return;

        if (delete.hasChild()) {
            if (delete.hasLeftChild() && delete.hasRightChild()) {
                //delete有两个Child
                BinaryNode<T> successor = delete.getSuccessor();
                delete.setData(successor.getData());
                if (successor.hasRightChild()) {
                    //successor有右子节点
                    successor.getRightChild().setParent(successor.getParent());
                    if (delete == root) return;
                    if (successor.isRightChildOf(successor.getParent()))
                        successor.getParent().setRightChild(successor.getRightChild());
                    else
                        successor.getParent().setLeftChild(successor.getRightChild());
                } else {
                    if (successor.isRightChildOf(successor.getParent()))
                        successor.getParent().setRightChild(null);
                    else
                        successor.getParent().setLeftChild(null);
                }
            } else {
                //delete有一个Child
                if(delete == root){
                    //delete是root
                    if(root.hasLeftChild()) root = root.getLeftChild();
                    else root = root.getRightChild();
                    root.setParent(null);
                }
                else{
                    //delete的父Node
                    BinaryNode<T> parent = delete.getParent();
                    if (delete.isLeftChildOf(parent)) {
                        if (delete.hasLeftChild()) {
                            parent.setLeftChild(delete.getLeftChild());
                            delete.getLeftChild().setParent(parent);
                        } else {
                            parent.setLeftChild(delete.getRightChild());
                            delete.getRightChild().setParent(parent);
                        }
                    } else {
                        if (delete.hasLeftChild()) {
                            parent.setRightChild(delete.getLeftChild());
                            delete.getLeftChild().setParent(parent);
                        } else {
                            parent.setRightChild(delete.getRightChild());
                            delete.getRightChild().setParent(parent);
                        }
                    }
                }
            }
        } else {
            //delete没有子Node(叶子Node)
            if (delete == root) root = null;
            else{
                //delete的父Node
                BinaryNode<T> parent = delete.getParent();
                if (delete.isLeftChildOf(parent)) parent.setLeftChild(null);
                else parent.setRightChild(null);
            }

        }

    }

    public BinaryNode<T> minimum() {
        if (root == null) return null;
        BinaryNode<T> current = root;
        while (current.getLeftChild() != null) current = current.getLeftChild();
        return current;
    }

    public BinaryNode<T> maximum() {
        if (root == null) return null;
        BinaryNode<T> current = root;
        while (current.getRightChild() != null) current = current.getRightChild();
        return current;
    }
}

/* * 基于链表实现树结构 */ package dsa; public class TreeLinkedList implements Tree { private Object element;//树根节点 private TreeLinkedList parent, firstChild, nextSibling;//父亲、长子及最大的弟弟 //(单节点树)构造方法 public TreeLinkedList() { this(null, null, null, null); } //构造方法 public TreeLinkedList(Object e, TreeLinkedList p, TreeLinkedList c, TreeLinkedList s) { element = e; parent = p; firstChild = c; nextSibling = s; } /*---------- Tree接口中各方法的实现 ----------*/ //返回当前节点中存放的对象 public Object getElem() { return element; } //将对象obj存入当前节点,并返回此前的内容 public Object setElem(Object obj) { Object bak = element; element = obj; return bak; } //返回当前节点的父节点;对于根节点,返回null public TreeLinkedList getParent() { return parent; } //返回当前节点的长子;若没有孩子,则返回null public TreeLinkedList getFirstChild() { return firstChild; } //返回当前节点的最大弟弟;若没有弟弟,则返回null public TreeLinkedList getNextSibling() { return nextSibling; } //返回当前节点后代元素的数目,即以当前节点为根的子树的规模 public int getSize() { int size = 1;//当前节点也是自己的后代 TreeLinkedList subtree = firstChild;//从长子开始 while (null != subtree) {//依次 size += subtree.getSize();//累加 subtree = subtree.getNextSibling();//所有孩子的后代数目 } return size;//即可得到当前节点的后代总数 } //返回当前节点的高度 public int getHeight() { int height = -1; TreeLinkedList subtree = firstChild;//从长子开始 while (null != subtree) {//依次 height = Math.max(height, subtree.getHeight());//在所有孩子中取最大高度 subtree = subtree.getNextSibling(); } return height+1;//即可得到当前节点的高度 } //返回当前节点的深度 public int getDepth() { int depth = 0; TreeLinkedList p = parent;//从父亲开始 while (null != p) {//依次 depth++; p = p.getParent();//访问各个真祖先 } return depth;//真祖先的数目,即为当前节点的深度 } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jinyihao823

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值