Java实现二叉查找树的创建、查找、插入、删除、遍历

  1. 首先创建节点类及其构造方法
  2. 编写插入方法,一方面可以完成二叉树的创建另一方面还可以完成节点的插入操作。
    • 首先确定根节点root
    • 比较要插入的数值和root中的数值大小,如果小于root中的数值则将其插入到左子树的适当位置,否则的话将其插入到右子树的适当位置。
  3. 查找:通过比较要查询的数值和root节点中的数值的大小来判断是该在左子树中查询还是在右子树中查询,以此类推。
  4. 删除的情况比较复杂
    • 如果删除的是叶子节点,则可以直接将其删除,只需要把其父节点对应的子节点置为空即可(如果要删除的节点是父节点的右子节点,则将父节点的右子节点置为空,同理处理删除节点为左子节点的情况)。
    • 如果要删除的节点只有左子树或者只有右子树:将要删除的节点的孩子节点赋值给其父节点对应的子节点即可(如果要删除的节点是其父节点的右孩子,则将要删除的节点的孩子节点赋值给其父节点的右孩子。同理处理为其父节点左孩子的情况。)
    • 要删除的节点既有左子树又有右子树:
      – 该结点右子树中的中序后继结点是该结点的右子结点
      – 该结点的右子树中的中序后继结点是该结点的右子树的左孙子结点(找到要删除的节点,以及在树的中序遍历中该节点的直接后继节点,让其后继节点替代要删除的节点,)
  5. 先序、中序、后序遍历则和之前的数组转换的二叉树的遍历方式相同。

完整代码如下:(可以自行构造二叉树结合注释理解代码原理)

package tree;

/**
 * @author xpengfei 实现二叉查找树的创建、查找、插入、删除、遍历
 */
// 节点类
class TreeNode {
    int data;
    TreeNode lchild; // 左孩子
    TreeNode rchild; // 右孩子

    // 构造方法,创建节点
    TreeNode(int data) {
        this.data = data;
        this.lchild = null;
        this.rchild = null;
    }

    // 输出节点中的数据
    public void displayNode() {
        System.out.print(this.data + "\t");
    }
}

public class BinarySearchTree {

    private TreeNode root; // 定义树的根节点

    /**
     * 根据数值,查找指定节点,并返回该节点
     * 
     * @param findNum
     *            要查找的数值
     * @return
     */
    public TreeNode findNode(int findNum) {
        TreeNode current;
        current = root;
        while (current != null) {
            if (findNum > current.data) {
                current = current.rchild;
            } else if (findNum < current.data) {
                current = current.lchild;
            } else {
                return current;
            }
        }
        return null;
    }

    /**
     * 获取当前节点的父节点
     * 
     * @param nowData
     *            当前数值
     * @return
     */
    public TreeNode getParentNode(int nowData) {
        TreeNode current;
        current = root;
        TreeNode parentNode = root;
        while (current != null) {
            if (nowData > current.data) {
                parentNode = current;
                current = current.rchild;
            } else if (nowData < current.data) {
                parentNode = current;
                current = current.lchild;
            } else { // 值相等时,返回parentNode 即为当前数值所在节点的父节点
                return parentNode;
            }
        }
        return null;
    }

    /**
     * 查找树的最小值节点
     * 
     * @param root
     *            根节点
     * @return
     */
    public TreeNode findMinNode(TreeNode root) {
        TreeNode node = root;
        while (node != null) {
            if (node.lchild != null) {
                node = node.lchild;
            } else {
                System.out.println("MinNode:" + node.data);
                return node; // 返回最小值所在的节点
            }
        }
        System.out.println("树为空!");
        return null;
    }

    /**
     * 查找树的最大值节点
     * 
     * @param root
     * @return
     */
    public TreeNode findMaxNode(TreeNode root) {
        TreeNode node = root;
        while (node != null) {
            if (node.rchild != null) {
                node = node.rchild;
            } else {
                System.out.println("MaxNode:"+node.data);
                return node; // 返回最大值所在的节点
            }
        }
        return null;
    }

    /**
     * 得到当前节点的直接后继节点,在删除用友左右孩纸节点的时候调用
     * 
     * @param currentNode
     *            当前节点
     * @return
     */
    public TreeNode getDirectNextNode(TreeNode currentNode) {
        TreeNode current = currentNode.rchild;
        while (current != null) {
            if (current.lchild != null) {
                current = current.lchild;
            } else {
                return current;
            }
        }
        return current;
    }

    /**
     * 删除指定的节点
     * 
     * @param data
     *            要删除的节点数值
     */
    public void deleteNode(int data) {
        TreeNode keyNode = findNode(data); // 先找到要删除的节点
        TreeNode parentNode = getParentNode(data); // 获取当前节点的父节点
        if (keyNode != null) { // 如果找到了要删除的节点
            // 第一种情况:要删除的节点的左右孩子都为空,即是没有左右子节点,要删除的为叶节点
            if (keyNode.lchild == null && keyNode.rchild == null) {
                if (keyNode == root) { // 该节点是根节点
                    root = null;
                    return;
                }
                if (parentNode.lchild == keyNode) { // 若是父节点的左子节点,则将其置为null,同理对待右节点
                    parentNode.lchild = null;
                } else {
                    parentNode.rchild = null;
                }
                return;
            } else if (keyNode.lchild == null) { // 第二种情况:要删除的节点没有左子树
                // 如果要删除的节点是父节点的左子节点,则将父节点的左子节点设置为要删除节点的右子节点
                if (parentNode.lchild == keyNode) {
                    parentNode.lchild = keyNode.rchild;
                } else {
                    // 如果要删除的节点是父节点的右子节点,则将父节点的右子节点设置为要删除节点的右子节点
                    parentNode.rchild = keyNode.rchild;
                }
                return;

            } else if (keyNode.rchild == null) { // 第三种情况:要删除的节点没有右子树
            // 如果要删除的节点是父节点的左子节点,则将要删除节点的左子节点赋值给父节点的左子节点
                if (parentNode.lchild == keyNode) {
                    parentNode.lchild = keyNode.lchild;
                } else {
                    parentNode.rchild = keyNode.lchild;
                }
                return;
            } else { // 第四种情况:要删除的节点既有左子树又有右子树
                TreeNode delNextNode = getDirectNextNode(keyNode);// 获取要删除节点的后继节点
                // 获取要删除节点的后继节点的父节点
                TreeNode getDelNextParentNode = getParentNode(delNextNode.data);
                // 1、该结点右子树中的中序后继结点是该结点的右子结点;
                if (delNextNode == keyNode.rchild) {
                    delNextNode.lchild = keyNode.lchild;
                    if (keyNode == parentNode.lchild) {
                        parentNode.lchild = delNextNode;
                    } else {
                        parentNode.rchild = delNextNode;
                    }
                } else {// 2、该结点的右子树中的中序后继结点是该结点的右子树的左孙子结点;
                    /*
                     * keyNode是当前要删除的节点
                     * parentNode是要删除的节点的父节点
                     * delNextNode是要删除节点的后继节点(在树的中序遍历序列中的直接后继节点)
                     * getDelNextParentNode是delNextNode的父节点
                     * 
                     * 原理是:找到要删除的节点,以及在树的中序遍历中该节点的直接后继节点,
                     *      让其后继节点替代要删除的节点,
                     */

                    if (parentNode.lchild == keyNode) {// 即要删除的节点是其父节点的左孩子
                        getDelNextParentNode.lchild = delNextNode.rchild;
                        delNextNode.rchild = keyNode.rchild;
                        parentNode.lchild = delNextNode;
                        delNextNode.lchild = keyNode.lchild;
                    } else {// 即要删除的节点是其父节点的右孩子
                        getDelNextParentNode.lchild = delNextNode.rchild;
                        delNextNode.rchild = keyNode.rchild;
                        parentNode.rchild = delNextNode;
                        delNextNode.lchild = keyNode.lchild;
                    }
                }
            }
        }
    }

    /**
     * 用insertNode方法,创建二叉树,并实现插入操作 插入指定的节点
     * 
     * @param data
     *            要插入的数值
     */
    public void insertNode(int data) {
        TreeNode node = new TreeNode(data); // 创建新节点
        TreeNode currentNode;
        if (root == null) { // 如果树为空,则将node设为根节点root
            root = node;
            return;
        } else {
            currentNode = root; // 将当前节点设为root,对其进行遍历
            while (true) {
                if (data >= currentNode.data) { // 如果数值大于当前节点的数值,则将其插入到右子树
                    if (currentNode.rchild == null) {
                        currentNode.rchild = node;
                        return; // 找到插入位置后返回.
                    } else { // 否则的话,将当前节点的右子树设为当前节点
                        currentNode = currentNode.rchild;
                    }
                } else { // 如果数值小于当前节点的数值,则将其插入到左子树
                    if (currentNode.lchild == null) {
                        currentNode.lchild = node;
                        return;
                    } else {
                        currentNode = currentNode.lchild;
                    }
                }
            }
        }

    }

    // 先序遍历
    public void preOrderTraverse(TreeNode node) {
        if(node!=null){
            System.out.print(node.data+"\t");
            preOrderTraverse(node.lchild);
            preOrderTraverse(node.rchild);
        }
    }

    // 中序遍历
    public void midOrderTraverse(TreeNode node) {
        if(node!=null){
            midOrderTraverse(node.lchild);
            System.out.print(node.data+"\t");
            midOrderTraverse(node.rchild);
        }
    }

    // 后序遍历
    public void lastOrderTraverse(TreeNode node) {
        if(node!=null){
            lastOrderTraverse(node.lchild);
            lastOrderTraverse(node.rchild);
            System.out.print(node.data+"\t");
        }
    }

    public static void main(String[] args) {
        BinarySearchTree tree=new BinarySearchTree();
        tree.root=new TreeNode(-1);
        tree.insertNode(-2);
        tree.insertNode(1);
        tree.insertNode(0);
        tree.insertNode(4);
        tree.insertNode(2);
        tree.insertNode(5);
        tree.insertNode(3);

        System.out.println("先序遍历:");
        tree.preOrderTraverse(tree.root);
        System.out.println();

        System.out.println("中序遍历:");
        tree.midOrderTraverse(tree.root);
        System.out.println();

        System.out.println("后序遍历");
        tree.lastOrderTraverse(tree.root);
        System.out.println();

        System.out.println("树中最小值;");
        tree.findMinNode(tree.root);
        System.out.println("树中最大值:");
        tree.findMaxNode(tree.root);

        tree.deleteNode(1);

        System.out.println("删除后的先序遍历:");
        tree.preOrderTraverse(tree.root);
        System.out.println();

        System.out.println("删除后的中序遍历:");
        tree.midOrderTraverse(tree.root);
        System.out.println();


    }

}


//-1 -2 1 0 4 2 3 5  
//-2 -1 0 1 2 3 4 5  
//-2 0 3 2 5 4 1 -1  
//min:-2  
//max:5  
//-1 -2 2 0 4 3 5  
//-2 -1 0 2 3 4 5 

结果如图:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值