数据结构是算法的基石;算法即为解决问题的实现思路、方案。
优秀的算法取决于采用的哪种数据结构。
数据结构分类:
数据结构
是指相互之间存在一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成。
常见的数据结构有:数组、字符串、栈、队列、链表、树、图、堆、散列表等,如下:
1. 数组、字符串
数组、字符串是最基本的数据结构,在很多编程语言中,二者的用法相似之处有很多。
数组是可以在内存中连续连续存储多个元素的结构,在内存中的分配也是连续的,访问是通过元素下标进行的,数据下标从 0 开始。
int [] data = new int[100];
data[0] = 1;
优点:
- 构建数组非常简单
- 按照索引查询元素速度快 —— 时间复杂度为 O(1)
- 按照索引遍历数据方便
缺点:
- 数组的大小确定后就不可以在扩容了,构建分配的空间必须连续
- 数组只能存储一种类型的数据
- 添加、删除的操作速度慢,因为要移动其他的元素 —— 时间复杂度为 O(n)
- 查询某个元素是否存在于数组中时需要遍历整个数组 —— 时间复杂度为 O(n)
适用场景:
- 频繁查询,对数组的空间要求不大,很少有增删操作的情况
2. 栈
栈是一种特殊的线性表,只允许在线性表的一端操作,即栈顶操作。
特点是:先进后出或者后进先出;从栈顶放入元素称为入栈,取出元素称为出栈。
实现方案:可以使用单链表来实现,利用单链表的头就能让所有的栈操作在 O(1) 时间内完成; 同样数组 + 指针 也可以实现,但效果(时间、空间复杂度)不太好。
栈结构可以比喻想集装桶里装小球一样。
使用场景:
- 针对只需要关心最近一次操作的相关问题,并且在操作完成后,需向前查找更前一次的操作。
3. 队列
队列也是一种线性表结构,可以跟现实中的排队类比,是先进先出。在队列尾部添加元素,队首取出元素。
实现方案:可以利用双链表来实现,其头指针实现队列头的查看、删除操作,尾指针实现队列尾的查看、删除操作。
使用场景:
- 因其先进先出的特点,在多线程阻塞队列管理中非常适用
- 需要按照一定顺序来处理数据,并且该数据量在不断的变化时,可以使用队列
- 广度优先搜索(Breadth-First-Search)运用队列较多
4. 双端队列
双端队列与普通队列不同的是,允许在队列的头尾两端以 O(1) 的时间来进行数据的查看和删除操作。
实现方案:可以利用双链表来实现。
应用场景:实现一个动态变化的窗口或连续空间
5. 链表
链表是物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表的指针地址实现。每个元素包含两个结点,一个是存储元素的数据域(内存空间),另一个是指向下一个结点地址的指针域。
根据指针的指向,链表能形成不同的结构:单链表、双向链表、循环链表等。
单链表:链表中每个元素结点即为一个单独的对象,这些对象都是通过对每个元素中的引用字段连接在一起的。
双链表:每个元素结点含有两个字段的引用。
优点:
- 链表是很常用的一种数据结构,不需要初始化容量,可以任意加减元素,灵活分配空间
- 添加或者删除元素时只需要改变前后两个元素结点的指针域指向地址即可,所以添加,删除很快 —— 用时 O(1),前提是单链表需知道前一个元素,双链表的话知道前/后元素均可
缺点:
- 因为含有大量的指针域,占用空间较大
- 查找元素需要从链表头开始遍历链表来查找,非常耗时 —— 用时 O(k) ,k表示第k个元素
适用场景:
- 数据量较小,数据元素个数不确定,需要频繁增加,删除操作的场景
6. 树
树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做 “树” 是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
考察点:往往可以考察队 递归 算法的熟练程度
常见:普通二叉树、平衡二叉树、完全二叉树、二叉搜索树、四叉树、多叉树。
它具有以下的特点:
- 每个节点有零个或多个子节点
- 没有父节点的节点称为根节点
- 每一个非根节点有且只有一个父节点
- 除了根节点外,每个子节点可以分为多个不相交的子树
常见的对树的讨论是 二叉树
二叉树的特点:
- 每个结点最多有两颗子树,结点的度最大为2
- 左子树和右子树是有顺序的,次序不能颠倒
- 即使某结点只有一个子树,也要区分左右子树
二叉树是一种比较有用的折中方案,它添加,删除元素都很快,并且在查找方面也有很多的算法优化,所以,二叉树既有链表的好处,也有数组的好处,是两者的优化方案,在处理大批量的动态数据方面非常有用。
树的遍历:
- 前序遍历
思路: 先访问根,然后左,最后右;同样在访问左、右子树的时候,同样是根左右的顺序,这是一个不断递归的过程;
场景: 树种进行搜索以及创建一棵新的树。 - 中序遍历
思路: 左跟右的访问顺序
场景: 最常见的是二叉搜索树
二次搜索树特点:左孩子小于根节点,根节点小于右孩子,因此二叉搜索树进行中序遍历后,得到的值就是有顺序的序列。
- 后序遍历
思路: 左右跟的访问顺序
场景: 对某个节点进行分析的时,需要来自左右子树的信息。收集信息操作是从树的底部不断往上进行的。
二叉树相关概念:
- 满二叉树: 一棵二叉树的结点要么是叶子结点,要么它有两个结点(如果一棵二叉树的层数为k,且结点总数为 2^k - 1,则为一棵满二叉树)
- 完全二叉树:若二叉树的深度为k,除第k层外,其他各层(1 ~ k-1层)的结点数目都是最大的,且第k层所有的结点都连续集中在左边,则为完全二叉树。
满二叉树也可以看做是一棵完全二叉树。
- 平衡二叉树:是一棵空树或者其左右子树深度之差不超过1,且左右子树都是一棵平衡二叉树。
平衡二叉树必须是一棵二叉搜索树 - 最优二叉树(哈夫曼树):树的带权路径长度达到最小,称其为最优二叉树(哈夫曼树)
哈夫曼树(Huffman Tree)是带权路径最短的树,权值较大的结点离根最近。
扩展:
二叉树有很多扩展的数据结构,包括平衡二叉树、红黑树、B+树等,这些数据结构二叉树的基础上衍生了很多的功能,在实际应用中广泛用到,例如mysql的数据库索引结构用的就是B+树,还有HashMap的底层源码中用到了红黑树。这些二叉树的功能强大,但算法上比较复杂,想学习的话还是需要花时间去深入的。
7.散列表
8. 堆
堆是一种比较特殊的数据结构,可以被看做一棵完全二叉树的数组对象,具有以下的性质:
- 堆中某个节点的值总是不大于或不小于其父节点的值
- 堆总是一棵完全二叉树
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。
适用场景:
- 因为堆有序的特点,一般用来做数组中的排序,称为堆排序。
9. 图
图是由结点的有穷集合V和边的集合E组成。其中,为了与树形结构加以区别,在图结构中常常将结点称为顶点,边是顶点的有序偶对,若两个顶点之间存在一条边,就表示这两个顶点具有相邻关系。
按照顶点指向的方向可分为无向图和有向图:
图是一种比较复杂的数据结构,在存储数据上有着比较复杂和高效的算法,分别有邻接矩阵 、邻接表、十字链表、邻接多重表、边集数组等存储结构。