美文网首页算法专家
查找-二叉搜索树(Java实现)

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

作者: tianma | 来源:发表于2016-04-13 01:32 被阅读699次

    版权声明:本文源自简书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

    相关文章

      网友评论

        本文标题:查找-二叉搜索树(Java实现)

        本文链接:https://www.haomeiwen.com/subject/nfpwlttx.html