实现二分搜索树

二分搜索树具有以下特点:

  1 二分搜索树是一颗二叉树
  2 二分搜索树每个节点的左子树的值都小于该节点的值,每个节点右子树的值都大于该节点的值
  3 任意一个节点的每棵子树都满足二分搜索树的定义

基于第二点我们的需要插入树的元素实现Comparable接口,下面实现二分搜索树的原型,这个类具有二分搜索树的基本成员变量和方法,之后再实现对元素的操作方法。

public class BST<E extends Comparable<E>> {

    private class Node{
        public E e;
        public Node left, right;

        public Node(E e){
            this.e = e;
            left = null;
            right = null;
        }
    }

    private Node root;
    private int size;

    public BST(){
        root = null;
        size = 0;
    }

    public int size(){
        return size;
    }

    public boolean isEmpty(){
        return size == 0;
    }
}

二分搜索树的添加

  向二分搜索树添加元素的方法使用递归很好实现,终止递归的条件是当前节点为null

// 向二分搜索树中添加新的元素e
    public void add(E e){
        root = add(root, e);
    }

    // 向以node为根的二分搜索树中插入元素e,递归算法
    // 返回插入新节点后二分搜索树的根
    private Node add(Node node, E e){

        if(node == null){
            size ++;
            return new Node(e);
        }

        if(e.compareTo(node.e) < 0)
            node.left = add(node.left, e);
        else if(e.compareTo(node.e) > 0)
            node.right = add(node.right, e);

        return node;
    }

二分搜索树的查询

  查询树中是否包含某元素的方法和插入的思路是一样的,用递归实现,当前节点为null时代表递归到底仍然没有找到,返回false,否则一旦找到将结束递归返回true

// 看二分搜索树中是否包含元素e
    public boolean contains(E e){
        return contains(root, e);
    }

    // 看以node为根的二分搜索树中是否包含元素e, 递归算法
    private boolean contains(Node node, E e){

        if(node == null)
            return false;

        if(e.compareTo(node.e) == 0)
            return true;
        else if(e.compareTo(node.e) < 0)
            return contains(node.left, e);
        else // e.compareTo(node.e) > 0
            return contains(node.right, e);
    }

接下来是对二分搜索树的遍历

  二分搜索树的遍历分为前序遍历,中序遍历和后序遍历,这里的前中后指的是根节点的访问顺序,前序先遍历根节点再遍历左右子树,中序是先遍历左子树再到根节点再到右子树,后序则是先遍历左右子树,再遍历根节点

  先实现前序遍历,使用递归非常容易实现,先遍历根节点再递归子树即可

// 二分搜索树的前序遍历
    public void preOrder(){
        preOrder(root);
    }

    // 前序遍历以node为根的二分搜索树, 递归算法
    private void preOrder(Node node){

        if(node == null)
            return;

        System.out.println(node.e);
        preOrder(node.left);
        preOrder(node.right);
    }

中序和后序只需要改变遍历语句的位置即可实现

 // 二分搜索树的中序遍历
    public void inOrder(){
        inOrder(root);
    }

    // 中序遍历以node为根的二分搜索树, 递归算法
    private void inOrder(Node node){

        if(node == null)
            return;

        inOrder(node.left);
        System.out.println(node.e);
        inOrder(node.right);
    }

    // 二分搜索树的后序遍历
    public void postOrder(){
        postOrder(root);
    }

    // 后序遍历以node为根的二分搜索树, 递归算法
    private void postOrder(Node node){

        if(node == null)
            return;

        postOrder(node.left);
        postOrder(node.right);
        System.out.println(node.e);
    }

二分搜索树的层序遍历

  层序遍历,顾名思义就是一层一层遍历,从根节点开始,然后第二层第三层以此类推,这里不需要递归,使用队列即可实现

  把每层元素入队,然后出队的时候再把出队节点的下一层子节点入队

// 二分搜索树的层序遍历
    public void levelOrder(){

        Queue<Node> q = new LinkedList<>();
        q.add(root);
        while(!q.isEmpty()){
            Node cur = q.remove();
            System.out.println(cur.e);

            if(cur.left != null)
                q.add(cur.left);
            if(cur.right != null)
                q.add(cur.right);
        }
    }

查找最大与最小节点

  首先需要知道一点,根据左子树一定比根节点小、右子树一定比根节点大这个原则可以知道最大节点的右子树必定为null,而最小节点的左子树也必定为null,知道这两点后查到最大最小节点的方法就非常容易实现,也是使用递归

// 寻找二分搜索树的最小元素
    public E minimum(){
        if(size == 0)
            throw new IllegalArgumentException("BST is empty!");

        return minimum(root).e;
    }

    // 返回以node为根的二分搜索树的最小值所在的节点
    private Node minimum(Node node){
        if(node.left == null)
            return node;
        return minimum(node.left);
    }

    // 寻找二分搜索树的最大元素
    public E maximum(){
        if(size == 0)
            throw new IllegalArgumentException("BST is empty");

        return maximum(root).e;
    }

    // 返回以node为根的二分搜索树的最大值所在的节点
    private Node maximum(Node node){
        if(node.right == null)
            return node;

        return maximum(node.right);
    }

删除最大与最小节点

  上面提到了最大节点的右子树必定为null,而最小节点的左子树也必定为null,所以删除最大节点的时候直接用其左子树代替被删节点,删除最小节点的时候直接用其右子树代替即可,用递归实现

// 从二分搜索树中删除最小值所在节点, 返回最小值
    public E removeMin(){
        E ret = minimum();
        root = removeMin(root);
        return ret;
    }

    // 删除掉以node为根的二分搜索树中的最小节点
    // 返回删除节点后新的二分搜索树的根
    private Node removeMin(Node node){

        if(node.left == null){
            Node rightNode = node.right;
            node.right = null;
            size --;
            return rightNode;
        }

        node.left = removeMin(node.left);
        return node;
    }

    // 从二分搜索树中删除最大值所在节点
    public E removeMax(){
        E ret = maximum();
        root = removeMax(root);
        return ret;
    }

    // 删除掉以node为根的二分搜索树中的最大节点
    // 返回删除节点后新的二分搜索树的根
    private Node removeMax(Node node){

        if(node.right == null){
            Node leftNode = node.left;
            node.left = null;
            size --;
            return leftNode;
        }

        node.right = removeMax(node.right);
        return node;
    }

删除任意节点

  删除任意节点的情况比较复杂, 当被删节点存在左右子树的时候是用什么元素替代被删节点的位置呢?这里我们使用被删节点的后继节点,即比被删节点右子树的最小节点,我们先把用上面的minimum()方法右子树后继节点找出来,然后用removeMin()方法删除后继节点并把删除后的右子树赋给后继节点作为后继节点的右子树,然后把被删节点的左子树原封不动的赋给后继节点作为后继节点的左子树,最后返回该树即可,被删节点的左右子树记得置空,让jvm回收。

 // 从二分搜索树中删除元素为e的节点
    public void remove(E e){
        root = remove(root, e);
    }

    // 删除掉以node为根的二分搜索树中值为e的节点, 递归算法
    // 返回删除节点后新的二分搜索树的根
    private Node remove(Node node, E e){

        if( node == null )
            return null;

        if( e.compareTo(node.e) < 0 ){
            node.left = remove(node.left , e);
            return node;
        }
        else if(e.compareTo(node.e) > 0 ){
            node.right = remove(node.right, e);
            return node;
        }
        else{   // e.compareTo(node.e) == 0

            // 待删除节点左子树为空的情况
            if(node.left == null){
                Node rightNode = node.right;
                node.right = null;
                size --;
                return rightNode;
            }

            // 待删除节点右子树为空的情况
            if(node.right == null){
                Node leftNode = node.left;
                node.left = null;
                size --;
                return leftNode;
            }

            // 待删除节点左右子树均不为空的情况

            // 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点
            // 用这个节点顶替待删除节点的位置
            Node successor = minimum(node.right);
            successor.right = removeMin(node.right);
            successor.left = node.left;

            node.left = node.right = null;

            return successor;
        }
    }

原文地址:https://www.cnblogs.com/skychmz/p/12026317.html

时间: 2024-10-31 22:23:26

实现二分搜索树的相关文章

并行二分搜索树bianry search tree

二分搜索树是一种设计良好的有序集合,在平衡的情况下,查找search,插入insertion,删除deletion都具有O(logn)的计算时间.本文讨论实现二分搜索树的具体细节. 二分搜索树的每个结点包含key域,以及至多两个孩子结点,并且左孩子小于当前结点的值,右孩子大于当前结点值.为了方便操作,每个结点还需要维护父结点的信息.从上面的描述可以看出,二分搜索树的任何一个子树同样也是二分搜索树. 搜索操作是所有操作的基础.搜索操作可以很容易用递归过程描述:key>cur.key,则搜索右子树:

[LeetCode] Closest Binary Search Tree Value II 最近的二分搜索树的值之二

Given a non-empty binary search tree and a target value, find k values in the BST that are closest to the target. Note: Given target value is a floating point. You may assume k is always valid, that is: k ≤ total nodes. You are guaranteed to have onl

二分搜索树的深度优先遍历和广度优先遍历

二分搜索树的特点 二分搜索树首先是一个二叉树,其次其必须满足的条件是:每个节点的键值必须大于其左子节点,每个节点的键值必须小于其右子节点,这样以左右孩子为根的子树仍为二分搜索树,需要注意的是,二分搜索树不一定是一颗完全二叉树. 深度优先遍历 深度优先遍历的基本思想:对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次.深度优先遍历的非递归的通用做法是采用栈.要特别注意的是,二分搜索树的深度优先遍历比较特殊,可以细分为前序遍历.中序遍历.后序遍历. 前序遍历:先访问当前节点,再依次

二分搜索树

  template <typename Key, typename Value> class BST{ private: struct Node{ Key key; Value value; Node *left; Node *right; Node(Key key, Value value){ this->key = key; this->value = value; this->left = this->right = NULL; } }; Node *root;

第二十六篇 玩转数据结构——二分搜索树

1.. 二叉树 跟链表一样,二叉树也是一种动态数据结构,即,不需要在创建时指定大小. 跟链表不同的是,二叉树中的每个节点,除了要存放元素e,它还有两个指向其它节点的引用,分别用Node left和Node right来表示. 类似的,如果每个节点中有3个指向其它节点的引用,就称其为"三叉树"... 二叉树具有唯一的根节点. 二叉树中每个节点最多指向其它的两个节点,我们称这两个节点为"左孩子"和"右孩子",即每个节点最多有两个孩子. 一个孩子都没有

《算法导论》动态规划—最优二分搜索树

案例 ?假如我们现在在设计一个英文翻译程序,要把英文翻译成汉语,显然我们需要知道每个单词对应的汉语意思.我们可以建立一颗二分搜索树来实现英语到汉语的关联.为了更快速地翻译,我们可以使用AVL树或者红黑树使每次查询的时间复杂度Θ(lgn),实际上对于字典翻译程序来说这么做存在一个问题,比如"the"这个单词经常用,却很有可能存在于十分远离树根的位置,而"machicolation"这种不常用的单词很可能存在于十分靠近树根的位置,这就导致查询频率高的单词需要的查询时间更

132.Find Mode in Binary Search Tree(二分搜索树的众数)

题目: Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently occurred element) in the given BST. 给定具有重复项的二叉搜索树(BST),找到给定BST中的所有众数(最频繁出现的元素). Assume a BST is defined as follows: 假设BST定义如下: The left subtree of a node

java——二分搜索树(递归、非递归)

~ import java.util.Stack; import java.util.LinkedList; import java.util.Queue; //二分搜索树 public class BST <E extends Comparable<E>> { private class Node{ public E e; public Node left, right; public Node(E e) { this.e = e; left = null; right = nu

十八 二分搜索树的三种遍历方式

三种遍历方式: package com.lt.datastructure.BST; public class BST<E extends Comparable<E>> { private class Node{ public E e; Node left,right; public Node(E e) { this.e = e; this.left = left; this.right = right; } } private Node root; private int size

十九 二分搜索树的广度优先遍历

二分搜索树广度优先遍历的实现: /* * 二分搜索树的层序遍历(广度优先遍历),队列实现 * 广度优先遍历优势在于更快找到想要查询的元素,主要用于搜索策略,算法设计--最短路径(无权图) */ public void levelOrder(){ Queue<Node> q = new LinkedList<>(); q.add(root); while(!q.isEmpty()){ Node cur = q.remove(); System.out.println(cur.e);