二叉树BT与二叉查找树BST概览

799 阅读5分钟

1、树

分析HashMap源码的时候,看到JDK8中链表的节点个数超过了泊松分布计算得出的8个节点时,链表将会被转化为红黑树,所以,在分享HaspMap的源码分析文章前,先来分享一下树的数据结构的相关知识。

概念 英文
tree
节点 node
edge
儿子 child
父亲 parent
root
树叶 leaf
兄弟 siblings
祖父 grandparent
孙子 grandchild
祖先 ancestor
后裔 descendant
真祖先 proper ancestor
真后裔 proper descendant
概念 英文 解释
节点到节点的路径 path 路径是节点到节点间所有节点组成的序列
路径的长 length 路径的长是路径上所有边的条数
节点的深度 depth 从根到节点的唯一路径的长
节点的高 height 节点到一片树叶最长路径的长
对数
对数是对求幂的逆运算
如果a的x次方等于N(a>0,且a不等于1),那么数x叫做以a为底N的对数(logarithm),记作x=logaN。其中,a叫做对数的底数,N叫做真数。

1.1、树的实现

将每个节点的所有儿子都放在树节点的链表中。

树的实现 箭头方向 英文
向下箭头 第一儿子 firstChild
水平箭头 下一兄弟 nextSibling

1.2、树的应用-文件目录

目录
文件名 节点
/
遍历 解释 英文
先序遍历 对节点的处理工作是在节点的所有儿子被处理之前进行的 preorder traversal
后续遍历 对节点的处理工作是在节点的所有儿子被处理之后进行的 postorder traversal

2、二叉树

概念 英文
二叉树 binary tree
平均/平衡二叉树 Balanced Binary Tree
二叉排序/查找/搜索树 binary search/sort tree

二叉树

每个节点都不能多于2个儿子的树

2.1、二叉树的实现

节点是由element元素的信息,加上两个到其他节点的引用left和right组成的结构。

class BinaryNode{
	Object element;
	BinaryNode left;
	BinaryNode right;
}

2.2、二叉树的应用-表达式树

表达式树expression tree的树叶是操作数operand,其他的节点为操作符operator。

中序遍历:左、节点、右。

构造表达式树 输入:a b + c d e + * * 输出:

3、二叉查找树 BST

查找树的ADT/Abstract Data Type/抽象数据类型,二叉排序/查找/搜索树。

二叉排序/查找/搜索树

空树或
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的结点。

平均深度,O(log N)

二叉查找树实现:

public class BinarySearchTree<T extends Comparable<T>> {
    //二叉查找树根节点
    private BinaryNode<T> root;


    //二叉树节点定义
    private static class BinaryNode<T extends Comparable<T>> {

        T element;  //节点值
        BinaryNode<T> left; //左节点
        BinaryNode<T> right; //右节点

        public BinaryNode(T key) {
            this(key, null, null);
        }

        public BinaryNode(T element, BinaryNode<T> left, BinaryNode<T> right) {
            this.element = element;
            this.left = left;
            this.right = right;
        }
    }
}

查找是否存在:


    /**
     * 查找是否存在
     *
     * @param search
     * @param root
     * @return
     */
    private boolean contains(T search, BinaryNode<T> root) {
        if (root == null) {
            return false;
        }

        int compareResult = search.compareTo(root.element);

        if (compareResult < 0) {
            return contains(search, root.left);
        } else {
            return contains(search, root.right);
        }
    }

查找最小值:

    /**
     * 查找最小值
     *
     * @param root
     * @return
     */
    private BinaryNode<T> findMin(BinaryNode<T> root) {

        if (root == null) {
            return null;
        }

        if (root.left == null) {
            return root;
        }

        return findMin(root.left);
    }

查找最大值:

    /**
     * 查找最大值
     *
     * @param root
     * @return
     */
    private BinaryNode<T> findMax(BinaryNode<T> root) {

        if (root == null) {
            return null;
        }

        if (root.right == null) {
            return root;
        }

        return findMax(root.right);
    }

插入值:


    /**
     * 插入值
     *
     * @param insert
     * @param root
     * @return
     */
    private BinaryNode<T> insert(T insert, BinaryNode<T> root) {

        if (root == null) {
            return new BinaryNode<T>(insert, null, null);
        }

        int compareResult = insert.compareTo(root.element);
        if (compareResult < 0) {
            root.left = insert(insert, root.left);
        } else if (compareResult > 0) {
            root.right = insert(insert, root.right);
        }

        return root;
    }

删除值:

    /**
     * 删除值
     * 如果是树叶,直接删除
     * 如果有一个儿子:该节点在其父节点调整自己的链以绕过该节点后被删除
     * 如果有两个儿子:用该节点右子树的最小的数据代替该节点的数据,并递归地删除该节点
     *
     * @param remove
     * @param root
     * @return
     */
    private BinaryNode<T> remove(T remove, BinaryNode<T> root) {
        if (root == null) {
            return root;
        }

        int compareResult = remove.compareTo(root.element);
        if (compareResult < 0) {
            root.left = remove(remove, root.left);
        } else if (compareResult > 0) {
            root.left = remove(remove, root.right);
        } else if (root.left != null && root.right != null) {//两个儿子
            root.element = findMin(root.right).element;
            root.right = remove(root.element, root.right);
        } else {
            root = (root.left != null) ? root.left : root.right;
        }
        return root;
    }

前序遍历:

/**
 * 前序遍历
 * @param node 待遍历二叉查找树BST根节点
 */
private void preOrder(BinaryNode<T> node) {
    if (node != null) {
        System.out.print(node.key + " ");
        preOrder(node.left);
        preOrder(node.right);
    }
}

中序遍历:

/**
 * 中序遍历
 * @param node 待遍历BST根节点
 */
private void midOrder(BinaryNode<T> node) {
    if (node != null) {
        midOrder(node.left);
        System.out.print(node.key + " ");
        midOrder(node.right);
    }
}

后序遍历:

/**
 * 后序遍历
 * @param node 待遍历BST根节点
 */
private void postOrder(BinaryNode<T> node) {
    if (node != null) {
        postOrder(node.left);
        postOrder(node.right);
        System.out.print(node.key + " ");
    }
}

4、AVL树

平均二叉/平衡二叉/AVL树

空树或每个节点的左右两子树高度差绝对值<=1的二叉查找树,每个节点的左右两子树都是平衡二叉树。
AVL树/Adelson-Velskii和Landis树是带有平衡条件的二叉查找树。
空树的高度定义为-1。
平衡条件必须要容易保持,它保证了树的深度须是
平均深度,O(根号N)

待续……

下一次树的分享将继续讲AVL树、伸展树、B树以及红黑树的相关知识……

欢迎关注Android技术堆栈,专注于Android技术学习的公众号,致力于提高Android开发者们的专业技能!

Android技术堆栈