查找-二叉搜索树(Java实现)

版权声明:本文源自简书tianma,转载请务必注明出处:http://www.jianshu.com/p/9155e2e9e8c1

前言

如果查找的数据集是有序的线性表,并且是顺序存储的,查找可以用折半查找、插值查找、斐波那契查找算法(详细算法见:有序表查找(折半、插值、斐波那契查找))等实现。但是正是因为他们是顺序的,所以在插入和删除操作中需要耗费大量时间,也就是说这些算法适合静态查找(只有查找操作),不适合动态查找(不仅有查找操作还有插入删除等操作)。而二叉搜索树正适合动态查找。

定义

二叉搜索树又称为二叉排序树,它或者是空树,或者是具有下列性质的二叉树:

  1. 如果它的左子树不为空,那么左子树的所有节点都小于根节点的值;
  2. 如果它的右子树不为空,那么右子树的所有节点都大于根节点的;
  3. 它的左、右子树也分别是二叉搜索树.

二叉树是递归定义的数据结构,其中序遍历是递增的有序序列。

操作

1. 插入
插入节点的过程是:若原二叉查找树为空,则直接插入;否则,若关键字 k 小于根节点关键字,则插入到左子树中,若关键字 k 大于根节点关键字,则插入到右子树中。注意每次插入的节点必是叶节点。

2. 删除
二叉查找树的删除操作是相对复杂一点,它要按 3 种情况来处理:

  • 若被删除节点 t 是叶子节点,则直接删除,不会破坏二叉排序树的性质;
  • 若节点 t 只有左子树或只有右子树,则让 t 的子树成为 t 父节点的子树,替代 t 的位置;
  • 若节点 t 既有左子树,又有右子树,则用 t 的直接前驱或者直接后继代替 t,然后从二叉查找树中删除这个后继,这样就转换成了第一或第二种情况。

3. 查找
查找是从根节点开始,若二叉树非空,将给定值与根节点的关键字比较,若相等,则查找成功;若不等,则当给定值小于根节点关键字时,在根节点的左子树中查找,否则在根节点的右子树中查找。
其查找平均时间复杂度为O(logn),但是最差情况为插入的节点是有序的,则该二叉搜索树会变成左斜树(或者右斜树或者可以理解为“链表”),即最差时间复杂度为O(n),故而查找性能不是严格意义上的O(logn),不稳定。

Java实现
public class SortedBinaryTree<E> {

    private Node<E> root; // 根节点

    private int size; // 二叉树元素个数

    /**
     * 二叉树节点
     */
    private static class Node<E> {
        E element; // 节点元素
        Node<E> lChild; // 左孩子
        Node<E> rChild; // 右孩子

        public Node(E element) {
            this(element, null, null);
        }

        public Node(E element, Node<E> lChild, Node<E> rChild) {
            this.element = element;
            this.lChild = lChild;
            this.rChild = rChild;
        }
    }

    public SortedBinaryTree(List<E> elements) {
        for (E e : elements) {
            add(e);
        }
    }

    public SortedBinaryTree(E[] elements) {
        for (E e : elements) {
            add(e);
        }
    }

    public SortedBinaryTree() {
    }

    /**
     * 判断当前元素是否存在于树中
     * 
     * @param element
     * @return
     */
    public boolean contains(E element) {
        return search(root, element);
    }

    /**
     * 递归搜索,查找当前以curRoot为根节点的树中element存在与否
     * 
     * @param curRoot
     * @param element
     * @return
     */
    @SuppressWarnings("unchecked")
    private boolean search(Node<E> curRoot, E element) {
        if (curRoot == null)
            return false;
        Comparable<? super E> e = (Comparable<? super E>) element;
        int cmp = e.compareTo(curRoot.element);
        if (cmp > 0) {
            // 查找的元素大于当前根节点对应的元素,向右走
            return search(curRoot.rChild, element);
        } else if (cmp < 0) {
            // 查找的元素小于当前根节点对应的元素,向左走
            return search(curRoot.lChild, element);
        } else {
            // 查找的元素等于当前根节点对应的元素,返回true
            return true;
        }
    }

    /**
     * 非递归搜索,查找当前以curRoot为根节点的树中的element是否存在
     * 
     * @param curRoot
     *            二叉排序树的根节点
     * @param element
     *            被搜索的元素
     * @param target
     *            target[0]指向查找路径上最后一个节点: 如果当前查找的元素存在,则target[0]指向该节点
     * @return
     */
    @SuppressWarnings("unchecked")
    private boolean find(Node<E> curRoot, E element, Node<E>[] target) {
        if (curRoot == null)
            return false;
        Node<E> tmp = curRoot;
        Comparable<? super E> e = (Comparable<? super E>) element;
        while (tmp != null) {
            int cmp = e.compareTo(tmp.element);
            target[0] = tmp;
            if (cmp > 0) {
                // 查找的元素大于当前节点对应的元素,向右走
                tmp = tmp.rChild;
            } else if (cmp < 0) {
                // 查找的元素小于当前节点对应的元素,向左走
                tmp = tmp.lChild;
            } else {
                // 查找的元素等于当前根节点对应的元素,返回true
                return true;
            }
        }
        return false;
    }

    /**
     * 向二叉排序树中添加元素,如果当前元素已经存在,则添加失败,返回false,如果当前元素不存在,则添加成功,返回true
     * 
     */
    @SuppressWarnings("unchecked")
    public boolean add(E element) {
        if (root == null) {
            root = new Node<E>(element);
            size++;
            return true;
        }
        Node<E>[] target = new Node[1];
        if (!find(root, element, target)) {
            // 当前元素不存在,插入元素
            // 此时target节点即为需要插入的节点的父节点
            Comparable<? super E> e = (Comparable<? super E>) element;
            int cmp = e.compareTo(target[0].element);
            Node<E> newNode = new Node<E>(element);
            if (cmp > 0) {
                // 插入的元素大于target指向的节点元素
                target[0].rChild = newNode;
            } else {
                // 插入的元素小于target指向的节点元素
                target[0].lChild = newNode;
            }
            size++;
            return true;
        }
        return false;
    }

    /**
     * 删除二叉排序树中的元素,如果当前元素不存在,则删除失败,返回false;如果当前元素存在,则删除该元素,重构二叉树,返回true
     * 
     * @param element
     * @return
     */
    @SuppressWarnings("unchecked")
    public boolean remove(E element) {
        Node<E>[] target = new Node[1];
        if (find(root, element, target)) {
            // 被删除的元素存在,则继续执行删除操作
            remove(target[0]);
            return true;
        }
        return false;
    }

    /**
     * 释放当前节点
     * 
     * @param node
     */
    private void free(Node<E> node) {
        node.element = null;
        node.lChild = null;
        node.rChild = null;
        node = null;
    }

    /**
     * 删除二叉排序树中指定的节点
     * 
     * @param node
     */
    private void remove(Node<E> node) {
        Node<E> tmp;
        if (node.lChild == null && node.rChild == null) {
            // 当前node为叶子节点,删除当前节点,则node = null;
            node = null;
        } else if (node.lChild == null && node.rChild != null) {
            // 如果被删除的节点左子树为空,则只需要重新连接其右子树
            tmp = node;
            node = node.rChild;
            free(tmp);
        } else if (node.lChild != null && node.rChild == null) {
            // 如果被删除的节点右子树为空,则只需要重新连接其左子树
            tmp = node;
            node = node.lChild;
            free(tmp);
        } else {
            // 当前被删除的节点左右子树均存在,不为空
            // 找到离当前node节点对应元素且最近的节点target(左子树的最右边节点 或者 右子树最左边节点)
            // 将node节点元素替换成target节点的元素,将target节点删除
            tmp = node; // tmp是target的父节点
            Node<E> target = node.lChild; // 找到左子树最大子树
            while (target.rChild != null) { // 在左子树中进行右拐
                tmp = target;
                target = target.rChild;
            }
            node.element = target.element; // node.element元素替换为target.element
            if (tmp == node) {
                // tmp == node 说明没有在左子树中进行右拐,也就是node节点的左孩子没有右孩子,
                // 需要重新连接tmp节点左孩子
                tmp.lChild = target.lChild;
            } else {
                // tmp != node, 进行了右拐,那么将重新连接tmp的右子树,将target.lChild赋值给tmp.rChild
                tmp.rChild = target.lChild;
            }
            // 释放节点
            free(target);
        }
        // 删除成功,size--;
        size--;
    }

    public int size() {
        return size;
    }

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

    public List<E> preOrderTraverse() {
        List<E> list = new ArrayList<E>();
        preOrderTraverse(root, list);
        return list;
    }

    private void preOrderTraverse(Node<E> curRoot, List<E> list) {
        if (curRoot == null)
            return;
        E e = curRoot.element;
        list.add(e);
        preOrderTraverse(curRoot.lChild, list);
        preOrderTraverse(curRoot.rChild, list);
    }

    public List<E> inOrderTraverse() {
        List<E> list = new ArrayList<E>();
        inOrderTraverse(root, list);
        return list;
    }

    private void inOrderTraverse(Node<E> curRoot, List<E> list) {
        if (curRoot == null)
            return;
        inOrderTraverse(curRoot.lChild, list);
        list.add(curRoot.element);
        inOrderTraverse(curRoot.rChild, list);
    }

    public List<E> postOrderTraverse() {
        List<E> list = new ArrayList<E>();
        postOrderTraverse(root, list);
        return list;
    }

    private void postOrderTraverse(Node<E> curRoot, List<E> list) {
        if (curRoot == null)
            return;
        inOrderTraverse(curRoot.lChild, list);
        inOrderTraverse(curRoot.rChild, list);
        list.add(curRoot.element);
    }

    /**
     * 返回中序遍历结果
     */
    @Override
    public String toString() {
        return inOrderTraverse().toString();
    }

    public static void main(String[] args) {
        Integer[] elements = new Integer[] { 62, 88, 58, 47, 73, 99, 35, 51, 93, 29, 37, 49, 56, 36, 48, 50 };
        SortedBinaryTree<Integer> tree = new SortedBinaryTree<Integer>(elements);
        System.out.println(tree);
        System.out.println(tree.contains(93));
        System.out.println(tree.size());
        System.out.println(tree.remove(47));
        System.out.println(tree.preOrderTraverse());
        System.out.println(tree.size());
    }

}

Github地址
SortedBinaryTree

    原文作者:tianma
    原文地址: https://www.jianshu.com/p/9155e2e9e8c1
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞