zoukankan      html  css  js  c++  java
  • java-二分查找树的实现

    package com.learn.tree.demo2;

    import java.util.LinkedList;
    import java.util.Queue;

    /**
    * 二分查找树BST(也叫二叉查找树、二叉排序树)的提出是为了提供查找效率,
    * 之所以称为二分查找树,因为该二叉树对应着二分查找算法,查找平均的时间复杂度为o(logn),所以该数据结构的提出是为了提高查找效率。 定义
    * 二分查找树或者是一棵空树,或者具有下列性质: 1.若它的左子树不为空,则左子树上所有结点的值均小于根结点的值;
    * 2.若它的右子树不为空,则右子树上所有结点的值均大于根结点的值; 3.它的左右子树均为二分查找树。
    *
    *
    * 具体实现:二叉树的前序遍历,中序遍历,后序遍历,层次遍历 求二叉树深度(根到节点-叶子节点最长路径) 新增节点(没有该键 则新增 有则添加) 删除节点
    *
    * 注意:该二叉搜索树的实现是没有重复键值的
    *
    */
    public class BinaryTree<Key extends Comparable<Key>, Value> {
    private Node root;// 二叉树的根节点
    private int count;// 二叉树中的节点数

    // 二叉树的实例化 的构造器
    public BinaryTree() {
    root = null;// 初始化根节点
    count = 0;
    }

    // 判断该 二叉树是否有节点
    public boolean isEmpty() {
    return count == 0;
    }

    // 该二叉树的节点数
    public int size() {
    return count;
    }

    // 新增二叉树的节点
    public void insert(Key key, Value value) {
    root = insert(root, key, value);
    }

    /**
    * 插入节点的内部实现方法 采用递归的方法实现 二叉树的插入操作 返回 新增节点的二叉树的后根节点(为什么是根节点 注意递归后的的递进,还有回退 )
    *
    * @param root
    * 该二叉树的根
    * @param key
    * @param value
    */
    private Node insert(Node node, Key key, Value value) {
    // 如果根节点的为空 ,则将新增的节点为 作为根节点
    if (node == null) {
    count++;
    return new Node(key, value);
    }
    if (key.compareTo(node.key) > 0) {
    node.right = insert(node.right, key, value);
    } else if (key.compareTo(node.key) < 0) {
    node.left = insert(node.left, key, value);
    } else
    node.value = value;
    return node;
    }

    /**
    * 获得最小节点
    *
    * @param key
    */
    public Node getMininum() {
    Node node = mininum(root);
    return node;
    }

    /**
    * 获得最小节点内部方法实现(没有左节点)--->迭代法
    *
    * @param node
    * @param key
    * @return
    */
    private Node mininum(Node node) {
    Node parent = null;
    while (node != null) {
    parent = node;
    node = node.left;
    }
    return parent;
    }

    /**
    * 获得最小节点内部方法实现(没有左节点)-->递归法
    *
    * @param node
    * @return
    */
    private Node minimumD(Node node) {
    if (node.left== null)
    return node;
    return minimumD(node.left);
    }

    /**
    * 获得的二叉树的最大节点
    *
    * @param key
    * @return
    */
    public Node getMaxinum() {
    Node node = maxinum(root);
    return node;
    }

    /**
    * 最大节点的内部实现(没有右节点)--->迭代法
    *
    * @param node
    * @param key
    * @return
    */
    private Node maxinum(Node node) {
    Node parent = null;
    while (node != null) {
    parent = node;
    node = node.right;
    }
    return parent;

    }

    /**
    * 最大节点的内部实现(没有右节点)--->递归法
    *
    * @param node
    * @return
    */
    private Node maxinumD(Node node) {
    if (node.right== null)
    return node;
    return maxinum(node.right);
    }

    /**
    * 删除二叉搜索树的最小节点
    *
    * @return
    */
    public Node removeMin() {
    if (root != null)
    root = removeMin(root);
    return root;
    }

    /**
    * 删除二叉搜索树的最小节点(不同的结构体 或者实体类实现方法不同) 这里是单项关联 过时将父节点的左节点 置为null,与左节点断开关联
    * 返回删除节点后新的二分搜索树的根 ----------------------->递归法
    *
    * @param node
    * @return
    */
    private Node removeMin(Node node) {
    if (node.left == null) {
    final Node rightNode = node.right;
    node.right = null;// let out gc
    count--;
    return rightNode;// 如果该节点有右节点,将该节点的 右节点 作为该
    // 节点的父节点的左节点(于此同时也断开了该节点与父节点的联系)
    }
    // 递归调用左节点(直至左节点为空 删除该节点)
    node.left = removeMin(node.left);
    return node;
    }

    /**
    * 删除二叉搜索树的最小节点--->迭代法
    *
    * @param node
    */
    private void removeMinNode(Node node) {
    Node parentNode = null;
    while (node != null) {
    parentNode = node;
    node = node.left;
    }
    final Node rightNode = node.right;
    node.right = null;// let out gc
    parentNode.left = rightNode;
    count--;
    }

    /**
    * 删除二叉搜索树的最大节点
    *
    * @return
    */
    public Node removeMax() {
    if (root != null)
    root = removeMax(root);
    return root;
    }

    /**
    * 删除二叉搜索树的最大节点(不同的结构体 或者实体类实现方法不同) 这里是单项关联 过时将父节点的右节点 置为null,与右节点断开关联
    * 返回删除最大节点后的二叉搜索树的节点 ----------------------->递归法
    *
    * @param node
    * @return
    */
    private Node removeMax(Node node) {
    if (node.right == null) {
    final Node nodeLeft = node.left;
    node.left = null;// let out gc
    count--;
    return nodeLeft;
    }
    node.right = removeMax(node.right);
    return node;
    }

    /**
    * 删除二叉搜索树的最大节点
    *
    * @param node
    */
    private void removeMaxNode(Node node) {
    Node parentNode = null;
    while (node != null) {
    parentNode = node;
    node = node.right;
    }
    final Node nodeLeft = node.left;
    node.left = null;// let out gc
    node.right = nodeLeft;
    count--;
    }

    /**
    * 删除二叉树的节点 设p指向待删除的结点,pre指向待删除结点的父亲,则删除操作视如下情况而定:
    * (1)若待删除的结点是叶子结点,不妨设pre->right=p(即待删除的结点为其父亲结点的右孩子),则直接删除p,对应为:pre->right=
    * NULL,delete p;
    * (2)若待删除的结点只有左子树或右子树,则只需将待删除结点的父亲与左孩子(或右孩子)连接起来,对应为,不妨设pre->right=p,
    * 以待删除结点仅有左子树的情况为例(右子树同理),对应为:pre->right=p->left,delete p;
    * (3)若待删除结点左右子树都有,则执行如下步骤:
    * 总体来说,整个线索是:找到右子树的最小值结点-->连接断开结点-->对最小值结点的上下文做善后工作。
    *
    * @param key
    */
    public void remove(Key key) {
    remove(root, key);
    }

    /**
    * 删除二叉树的节点 内部实现 使用递归的方法 返回删除后的二叉树的根节点--迭代法 返回该节点
    *
    * @param node
    * @param key
    * @return
    */
    private Node remove(Node node, Key key) {
    Node parent = root;
    while (node != null) {
    if (key.compareTo(node.key) > 0) {
    parent = node;
    node = node.right;
    } else if (key.compareTo(node.key) < 0) {
    parent = node;
    node = node.left;
    } else
    break;// 当键相等的时候 就查到该节点 此时node就是 要 删除的节点,与parent 断开关联
    }
    if (node == null)
    throw new RuntimeException("没有该键" + key + "对应的节点!");
    // 要删除的左节点左节点不存在
    if (node.left == null) {
    final Node rihtNode = node.right;
    if (node.key.compareTo(parent.key) > 0)
    parent.right = rihtNode;
    else
    parent.left = rihtNode;
    node.right = null;
    count--;
    return node;
    }
    // 要删除的右节点不存在(其中左右都为null不用考虑,处理包含在其中)
    if (node.right == null) {
    final Node leftNode = node.left;
    if (node.key.compareTo(parent.key) > 0)
    parent.left = leftNode;
    else
    parent.right = leftNode;
    node.left = null;
    count--;
    return node;
    }
    // 要删除的左右节点都存在
    if (node.right != null && node.left != null) {
    // 找到该替换该节点的节点
    Node successor = new Node(mininum(node.right));
    successor.right = removeMin(node.right);
    if (node.key.compareTo(parent.key) > 0)
    parent.right = successor;
    else
    parent.left = successor;
    successor.left = node.left;
    node.left = null;// let out gc
    node.right = null;// let out gc
    return node;
    }
    return node;
    }

    /**
    * 删除节点------->递归法 返回删除节点后二叉搜索树的根节点
    *
    * @param node
    * @param key
    * @return
    */
    private Node removeNode(Node node, Key key) {
    if (node == null)
    return node;
    if (key.compareTo(node.key) > 0) {
    node.right = removeNode(node.right, key);
    return node;
    } else if (key.compareTo(node.key) < 0) {
    node.left = removeNode(node.left, key);
    return node;
    } else {
    if (node.left == null) {
    final Node rNode = node.right;
    node.right = null;
    count--;
    return rNode;
    }
    if (node.right == null) {
    final Node lNode = node.left;
    node.left = null;
    count--;
    return lNode;
    }
    if (node.right != null && node.left != null) {
    Node successor = new Node(mininum(node.right));
    successor.right = removeMin(node.right);
    successor.left = node.left;
    node.left = null;
    node.right = null;
    return successor;
    }
    return node;
    }
    }

    /**
    * 通过键 key获取节点
    *
    * @param key
    * @return
    */
    public Node node(Key key) {
    Node node = node(root, key);
    return node;
    }

    /**
    * 用迭代法实现-通过键是获取节点
    *
    * @param node
    * @param key
    * @return
    */
    private Node node(Node node, Key key) {
    while (node != null) {
    if (key.compareTo(node.key) > 0)
    node = node.right;
    else if (key.compareTo(node.key) < 0)
    node = node.left;
    else
    return node;
    }
    return null;
    }

    /**
    * 判断该键是否在 二叉搜索树
    *
    * @param key
    * @return
    */
    public boolean contain(Key key) {
    return contain(root, key);
    }

    /**
    * 递归法
    *
    * @param node
    * @param key
    * @return
    */
    private boolean contain(Node node, Key key) {
    if (node == null)
    return false;
    if (key.compareTo(node.key) == 0)
    return false;
    else if (key.compareTo(node.key) > 0)
    return contain(node.right, key);
    else
    return contain(node.left, key);
    }

    /**
    * 前序遍历(深度遍历的一种) Traversal
    */
    public void preOrder() {
    if (root != null)
    preOrder(root);
    }

    /**
    * 满足 根-->左--->右 (递归法)
    *
    * @param node
    */
    private void preOrder(Node node) {
    if (node != null)
    return;
    System.out.println("节点键的为:" + node.key + " 节点的值为:" + node.value);
    preOrder(node.left);
    preOrder(node.right);
    }

    /**
    * 中序遍历(深度遍历的一种)Traversal 中序遍历二叉搜索树 满足从小到大顺序 (排序)
    */
    public void inOrder() {
    if (root != null)
    inOrder(root);
    }

    /**
    * 中序遍历 --->递归法
    *
    * @param node
    */
    private void inOrder(Node node) {
    if (node == null)
    return;
    inOrder(node.left);
    System.out.println("节点键的为:" + node.key + " 节点的值为:" + node.value);
    inOrder(node.right);
    }

    /**
    * 后序遍历(深度遍历的一种)Traversal
    */
    public void postOrder() {
    if (root != null)
    postOrder(root);
    }

    /**
    * 中序遍历 --->递归法
    *
    * @param node
    */
    private void postOrder(Node node) {
    if (node == null)
    return;
    postOrder(node.left);
    postOrder(node.right);
    System.out.println("节点键的为:" + node.key + " 节点的值为:" + node.value);
    }

    /**
    * 层序遍历(广度优先遍历)
    *
    * @param node
    */
    public void levelOrder() {
    if (root != null)
    levelOrder(root);
    }

    /**
    * 利用队列 (Queue)先进先出
    *
    * @param node
    */
    private void levelOrder(Node node) {
    Queue<Node> queue = new LinkedList<Node>();
    queue.offer(node);
    while (!queue.isEmpty()) {
    Node element = queue.poll();
    System.out.println("节点键的为:" + element.key + " 节点的值为:" + element.value);
    if (element.left != null)
    queue.offer(element.left);
    if (element.right != null)
    queue.offer(element.right);
    }
    }

    /**
    * 二叉树的节点用一个内部类来实现
    *
    * @author caibixiang
    *
    */
    private class Node {
    private Key key;// 键 (满足根节点的键小于右节点中所有节点,大于左节点的中所有节点)
    private Value value;// 值
    private Node left;// 左节点
    private Node right;// 又节点

    private Node(Key key, Value value) {
    this.key = key;
    this.value = value;
    this.left = null;
    this.right = null;
    }

    private Node(Node node) {
    this.key = node.key;
    this.value = node.value;
    this.left = node.left;
    this.right = node.right;
    }

    }

    public static void main(String[] args) {
    int N = 100;
    // 创建一个数组,包含[0....N]的所有的元素
    Integer[] arr = new Integer[N];
    for (int i = 0; i < N; i++)
    arr[i] = new Integer(i);
    // 打乱数组顺序
    for (int i = 0; i < N; i++) {
    int pos = (int) (Math.random() * (i + 1));
    Integer t = arr[pos];
    arr[pos] = arr[i];
    arr[i] = t;
    }
    // 由于我们实现的二分搜索树不是平衡二叉树
    // 所以如果按照顺序插入一组数据,我们的二分搜索树会退化成为一个链表
    // 平横二叉树的实现下一节来实现(avl树)
    BinaryTree<Integer, String> bst = new BinaryTree<Integer, String>();
    for (int i = 0; i < N; i++)
    bst.insert(new Integer(arr[i]), Integer.toString(arr[i]));
    bst.inOrder();// 升序排列
    bst.remove(5);
    bst.remove(7);
    bst.remove(71);
    bst.remove(23);
    bst.remove(34);
    bst.remove(56);
    System.out.println(bst.size());
    bst.inOrder();// 升序排列

    }

    }

  • 相关阅读:
    ReSharper.8.0.14.856注册码
    asp.net 301重定向代码
    WebResource.axd 404 错误
    【原创】asp.net导出word 检索 COM 类工厂中 CLSID 为 {000209FF-0000-0000-C000-000000000046} 的组件失败,原因是出现以下错误: 8000401a
    C#操作word或excel及水晶报表,检索 COM 类工厂中 CLSID 为 {} 的组件时失败,原因是出现以下错误: 80070005
    Repository 设计模式介绍
    将15位身份证转换成18位
    清理数据库木马文件
    Oracle的创建表空间及用户
    使用python脚本执行地理处理工具
  • 原文地址:https://www.cnblogs.com/caibixiang123/p/8244615.html
Copyright © 2011-2022 走看看