为什么要有树结构
树结构本身是一种天然的组织,比如公司的组织架构,比如我们的代码结构,图书的索引等。都是一种数结构。
树最强大的功能是进行储存查询,为此计算机科学家们创造了诸如红黑树,AVL树,B+树,字典树,堆,并查集等数据结构,它们被广泛地运用于计算机的各个方面。
- 一颗较均匀的树其高度∼Θ(log|V|),查询十分高效
- 树的空间复杂度为Θ(|V|),利于储存
- 树的性质可以一直被维护,能设计出持久化储存管理数据的结构
- 树可以递归定义,其子树也满足定义,便于在增删的时候调整结构以维护树的定义
- 树的扩展性强,通过微调可以适应环境的变化。
- 树能可以展现偏序关系(dfs序)和全序关系(中序)
二分搜索树基础
二分搜索树基础 - 二叉树
再说二分搜索树之前,先来看一下树结构中二叉树。这个是二分搜索树的基础。
二叉树的特点
- 二叉树具有唯一根节点
- 二叉树中,每个节点最多有两个子结点,分别为左孩子和右孩子
- 没有孩子的节点,称为叶子节点(叶子节点不一定在最底层)
- 每个节点最多只有一个父节点
- 二叉树不一定是满的
二叉树的进化 - 二分搜索树
二分搜索树继承了二叉树的全部特性。并且新增了一些特性。
- 每个节点的值大于左子树的所有节点的值
- 每个节点的值小于右子树的所有节点的值
- 每一颗子树也是二分搜索树
- 二分搜索树具有顺序性,存储的数据具有可比较性(也就是说二分搜索树是存在局限性的)
定义一颗简单的二分搜索树
package com.yunhuxi.cloudeyes.util.data.structure;
public class BST<E extends Comparable<E>> {
private class Node {
//定义节点属性,左孩子,右孩子,以及数据对象范型E
public E e;
public Node left, right;
public Node(E e) {
this.e = e;
left = null;
right = null;
}
}
//定义一个Node节点root作为根节点, size作为树的节点数量
private Node root;
private int size;
//使用构造方法初始化参数
public BST(){
root = null;
size = 0;
}
//获取数量
public int size(){
return size;
}
//空判断
public boolean isEmpty(){
return size == 0;
}
}
- 从这段代码可以看到,我们的BST使用了范型来兼容多种数据对象类型,实现了Comparable接口进行排序。
- 注意Comparable的E是必须要和前面的E一致,防止出现类型不匹配导致的对象转换错误。
通过二分搜索树的添加操作来深入了解递归的终止条件
我们可以把一颗二叉树的每组根节点和叶子结点看成是二叉树的最小单元,这样我们就简化了左孩子和右孩子的添加操作。这样我们解的问题就变成了一个向三角形插入数据的问题。本质上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);
}
if(e.compareTo(node.e) > 0) {
node.right = add(node.right, e);
}
return node;
}
注意左孩子和右孩子的插入,只是在这里做了递归操作。
if(e.compareTo(node.e) < 0) {
node.left = add(node.left, e);
}
if(e.compareTo(node.e) > 0) {
node.right = add(node.right, e);
}