什么是数据结构?数据结构是计算机存储、组织数据的方式。数据的基本功能是对数据的增删改查,不同的数据结构效率和侧重点不一样。
常见数据结构
数据结构 | 优点 | 缺点 |
---|---|---|
数组 | 随机访问快 | 删除、指定位置插入慢 |
链表 | 插入和删除快 | 随机访问慢 |
树 | 查找快 | 删除比较复杂 |
图 | 适用多对多 | 算法复杂 |
Hash | 查询速度快 | 需要能够Hash |
索引 | 检索速度快 | 空间换时间 |
Java基础数据结构 - Collection
Collection大类分为Set、List、Queue。其他分支都是从这些分类中分离出来的。
Set
HashSet 使用Hash实现
LinkedHashSet 使用Hash和链表实现
TreeSet 内部红黑树的实现List
ArrayList 数组列表
Stack 栈
Vector 动态数组(淘汰)
LinkList 链表的形式实现Queue
PriorityQueue 优先队列(不能为空,元素可比较)
BlockingQueue 阻塞队列(接口)
Deque 双向队列(接口)
其他衍生
Java基础数据结构 - Map
Map用于Key-Value(键值对)结构。
总结Collection和Map
类 | 数据逻辑 | 存储方式 | 线程安全 | 查询 | 插入 | 删除 |
---|---|---|---|---|---|---|
ArrayList | 线性 | 顺序存储 | 不安全 | O(1) | O(n) | O(n) |
LinkList | 线性 | 链式存储 | 不安全 | O(n) | O(1) | O(1) |
HashSet | 集合 | Hash存储 | 不安全 | O(1) | O(1) | O(1) |
LinkHashSet | 集合 | Hash+链式存储 | 不安全 | O(1) | O(1) | O(1) |
TreeSet | 树 | 链式存储 | 不安全 | O(log(n)) | O(log(n)) | O(log(n)) |
PriorityQueue | 完全二叉树 | 顺序 | 不安全 | |||
BlockingQueue | 线性 | 链表 | 安全 | |||
ConcurrentLinkedQueue | 线性 | 链表 | 安全 | |||
HashMap | 链表 | 不安全 | O(1) | O(1) | O(1) | |
HashTable | 链表 | 安全 | O(1) | O(1) | O(1) | |
TreeMap | 红黑树 | 链表 | 不安全 | O(log(n)) | O(log(n)) | O(log(n)) |
ConcurrentSkipListMap | 跳表 | 索引+链表 | 安全 | O(log(n)) | O(log(n)) | O(log(n)) |
跳表
跳表是一种随机化数据结构,目前Redis和LevelDB在使用。实现原理比红黑树和AVL数简单,其性能不相上下。存储结构使用索引+链表方式实现。
跳表就是用空间换时间。上图,如果在线性表中查找32这个数字,需要比对前面的一串数字,至少5次。跳表就是将某个数随机提取出来作为索引,每次提取出来的是当前一次索引。如上,提取7和21作为第二级索引,21作为第三级索引,所以比对的时候从最高级索引一级一级比对。当需要查找32时,只需要3次。
B树
B树又叫二叉搜索树,是用二分法查找。特点:
- 所有非叶子结点至多拥有两个儿子(Left和Right)
- 所有结点存储一个关键字
- 非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树
B-树
先了解两个概念:树的阶 和 节点的度。树的阶:指的是一个结点最多能有多少棵子树,比如二叉树的阶(M)就是2。节点的度:对于一个节点,有n个边和它相连,就叫做度数=n,树的阶用来限制节点的度。
B树的产生是为了减少搜索的次数,提高查询效率。B树的性质如下:
- 任意非叶子节点最多有M个儿子;且M>2;
- 根结点的儿子数为[2,M];
- 除根结点以外的非叶子结点的儿子数为[M/2, M];
- 每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字)
- 非叶子结点的关键字个数=指向儿子的指针个数-1;
- 非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
- 非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;
- 所有叶子结点位于同一层
B树的特点就是关键字可比较,并且唯一、不为空。所以搜索数据就像二分法查找一样,从跟节点开始查找,如果命中则结束,不命中则在关键字范围内的子节点中查找。
B+树
B+树是B树的变体,这种更适合文件索引系统,非子叶节点相当于存储索引,而不是数据。不同点:
- 非叶子结点的 子树指针=关键字数相同
- 非叶子结点的子树指针P[i],指向关键字值属于 [K[i], K[i+1]) 的子树
- 为所有叶子结点增加一个链指针
- 所有关键字都在叶子结点出现
B*树
B树是B+树的变体。在B+Tree的非根和非叶子结点再增加指向兄弟的指针。当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中。所以B树的效率会比B+树低,但是空间效率高。
红黑树
红黑树是一种近似平衡的二叉查找树,它能够确保任何一个节点的左右子树的高度差不会超过二者中较低那个的一陪。具体来说,红黑树是满足如下条件的二叉查找树:
- 每个节点要么是红色,要么是黑色
- 根节点必须是黑色
- 红色节点不能连续(也即是,红色节点的孩子和父亲都不能是红色)
- 对于每个节点,从该点至null(树尾端)的任何路径,都含有相同个数的黑色节点
正如第三点所述,当查找树的结构发生改变时,红黑树的条件可能被破坏,需要通过调整使得查找树重新满足红黑树的条件。调整可以分为两类:一类是颜色调整,即改变某个节点的颜色;另一类是结构调整,即改变检索树的结构关系。结构调整过程包含两个基本操作:左旋(Rotate Left),右旋(RotateRight)。
左旋:左旋的过程是将某个节点(x)的右子树绕该节点逆时针旋转,使得x的右子节点成为x的父亲,同时修改相关节点的引用。旋转之后,二叉查找树的属性仍然满足。
右旋:与左旋相似,是将节点顺时针旋转。
红黑树这么复杂,为什么还要用呢?红黑树的厉害在于有着良好的最坏情况运行时间,他的最终查找、插入、删除的时间复杂度最坏情况下依然为O(logn)。Java中TreeSet和TreeMap都用红黑树实现。
AVL树
叫做平衡二叉树。它或者是一颗空树,或者具有以下性质的二叉树:它的左子树和右子树的深度之差(平衡因子)的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树。
二叉树的不平衡导致查询的时间上大大加长,所以才有了平衡树。所以每次的插入和删除都要确保二叉树的平衡。这里引入了平衡因子(左子节点的深度减去右子节点的深度的差值,所以只要-1,0,1),根据平衡因子来左旋和右选调整,使之平衡。
AVL树的插入删除操作会占用大量的时间,所以如果业务场景是查多插少的业务,可以使用AVL树,否者推荐红黑树。
Splay 树
Splay树也是一个平衡二叉树,与AVL树不同的是,Splay树以某个关键字搜索次数为权值(关键字的热度),热度越大越靠近根节点,这样需要搜索到该关键字的搜索次数就降低,从而提升效率。Splay树会在一定条件下牺牲平衡来提高热度。