文章目录
- 数据结构
- 一、概念
- 1、时间复杂度与空间复杂度
- 2、常见算法时间复杂度
- 3、Comparable
- 二、常见的排序算法
- 1、直接插入排序
- 2、希尔排序
- 3、选择排序
- 4、堆排序
- 5、冒泡排序
- 6、快速排序
- 7、归并排序
- 8、二分查找算法
- Q:什么时候需要结束呢?
- 三、线性表
- 1、概念
- 2、顺序表
- 3、链表
- 4、栈
- 5、队列
- 6、栈和队列的区别
- 四、树
- 1、概念
- 2、二叉树
- 3、平衡树
- 1、2-3查找树
- 2、红黑树
- 4、B树
- 5、B+树
- 五、堆
- 六、优先队列
- 七、图
- 1、图的分类
- 2、无向图的相关术语
- 3、无向图的搜索算法
- 4、有向图的相关术语
数据结构
一、概念
1、时间复杂度与空间复杂度
2、常见算法时间复杂度
复杂度由低到高是:O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3)
3、Comparable
Java提供的一个用来定义排序规则的接口。
二、常见的排序算法
参考链接: 常见七种排序算法对比(超全!!!)
插入排序——数据少或基本有序;选择排序——数据少(或优于插入);希尔排序——数据中等;堆排序——数据多;归并排序——数据多且排序稳定。
1、直接插入排序
排序原理: 将数组分为两部分(有序和无序),将后部分元素逐一插入前部分有序元素的适当位置。
适用场景: 待排序 记录较少或基本有序 的情况。
2、希尔排序
排序原理: 希尔排序(Shell Sort)是插入排序的一种,针对直接插入排序算法进行改进。先选定一个 整数增量,把待排序文件中所有记录分成个组 ,所有距离为增量的记录分在同一组内,并对每一组内的记录进行排序。然后减小增量,重复上述分组和排序的工作。当增量减至1时,所有记录排序完毕。
适用场景: 中等规模的数据量,对规模很大的数据量不是最佳选择。
3、选择排序
排序原理: 每次遍历一趟,找出最小的数,放到最前端,直到全部待排序的数据元素排完。 (这里说的是最前,是指无序的队列中的最前)
适用场景: 数据量较小的情况,比直接插入排序稍快。
4、堆排序
排序原理: 将待排序记录看作完全二叉树,可以建立大根堆或小根堆,大根堆中每个节点的值都不小于它的子节点值,小根堆中每个节点的值都不大于它的子节点值。
适用场景: 数据量较大的情况。
5、冒泡排序
排序原理: 比较相邻的元素,如果第一个比第二个大就进行交换 ,对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对,每一轮排序后末尾元素都是有序的,针对n个元素重复以上步骤n-1次排序完毕。
6、快速排序
排序原理: 首先选择一个 基准元素 ,通过一趟排序将要排序的数据分割成独立的两部分,一部分全部小于等于基准元素,一部分全部大于等于基准元素,采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度等于1,代表已经有序,或者小区间的长度等于0,代表没有数据。
7、归并排序
排序原理: 应用分治法将待排序序列分成两部分,然后对两部分分别递归排序,最后进行合并,使用一个辅助空间并设定两个指针分别指向两个有序序列的起始元素,将指针对应的较小元素添加到 辅助空间 ,重复该步骤到某一序列到达末尾,然后将另一序列剩余元素合并到辅助空间末尾。
适用场景: 数据量大且对稳定性有要求的情况。
8、二分查找算法
对于一个有序的升序列表,将目标值与表中间的值进行对比:
① 如果目标值与表中间的值相等,则直接返回表中间的值即可。
② 如果目标值与表中间的值不相等,则将两者进行大小比较,从而分成两个表:
- 如果目标值小于中间值,说明目标值在中间值往左的表中,故舍弃中间值往右的表,重新二分查找中间值往左的表。
- 如果目标值大于中间值,说明目标值在中间值往右的表中,故舍弃中间值往左的表,重新二分查找中间值往右的表。
最后,重复以上过程,直到找到目标值为止;或直到子表不存在为止,此时为查找不成功。
算法思路:
① 首先确定有序的升序列表的中间值是多少,即:mid = (left+right)/2 //中间值的下标
② 将目标值target与表中间的值arr[mid]进行比较:
- 如果target = arr[mid],说明目标值与arr[mid]相等,找到了目标值。
- 如果target < arr[mid],说明目标值在arr[mid]的左边,因此需要接着二分查找左边的表。
- 如果target > arr[mid],说明目标值在arr[mid]的右边,因此需要接着二分查找右边的表。
Q:什么时候需要结束呢?
① 找到目标值就结束。
② 查找完整个数组,如果依然没有找到目标值,也需要结束。
③ 当left > right,则直接退出。
三、线性表
1、概念
线性表是最基本、最简单、也是最常用的一种数据结构。一个线性表是n个具有相同特性的数据元素的有限序列。
前驱元素: 若A元素在B元素的前面,则称A为B的前驱元素。
后继元素: 若B元素在A元素的后面,则称B为A的后继元素。
线性表的特征: 数据元素之间具有一种"—对一"的逻辑关系。
①第一个数据元素没有前驱,这个数据元素被称为头结点。
②最后一个数据元素没有后继,这个数据元素被称为尾结点。
③除了第一个和最后一个数据元素外,其他数据元素有且仅有一个前驱和一个后继。
线性表的分类
线性表中数据存储的方式可以是顺序存储,也可以是链式存储,按照数据的存储方式不同,可以把线性表分为顺序表和链表。
2、顺序表
顺序表在计算机内存中以 数组 的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元,依次存储线性表中的各个元素、使得线性表中 在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,如ArrayList。
3、链表
链表是一种 物理存储单元上非连续、非顺序的存储结构,其物理结构不能直观的表示数据元素的逻辑顺序,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列的结点(链表中的每一个元素称为结点)组成,结点可以在运行时动态生成,如LinkedList。
-
单向链表
单向链表是链表的一种,它由多个结点组成,每个结点都由一个数据域和一个指针域组成,数据域用来存储数据,指针域用来指向其后继结点。链表的头结点的数据域不存储数据,指针域指向第一个真正存储数据的结点。
-
双向链表
双向键表也叫双向表,是链表的一种,它由多个结点组成,每个结点都由一个数据域和两个指针域组成,数据域用来存储数据,其中一个指针域用来指向其后继结点,另一个指针域用来指向前驱结点。链表的头结点的数据域不存储数据,指向前驱结点的指针域值为null,指向后继结点的指针域指向第一个真正存储数据的结点。
-
常见笔试题:链表反转
-
循环链表
循环链表,顾名思义,链表整体要形成一个圆环状。在单向链表中,最后一个节点的指针为null,不指向任何结点,因为没有下一个元素了。要实现循环链表,我们只需要让单向链表的最后一个节点的指针指向头结点即可。
循环链表的应用: 约瑟夫问题(一群人围在一起报数)
4、栈
栈是一种基于 先进后出(FILO) 的数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。
我们称数据进入到栈的动作为 压栈 ,数据从栈中出去的动作为 弹栈 。
栈的核心操作:入栈,出栈,取栈顶元素。
对于栈的形象理解:子弹的弹夹我们一定见过,子弹在被压入的时候就相当于是一个个元素,而弹夹就相当于是栈。先被压入的子弹是最后被打出的,先压入的元素是最后出来的,也就是先进后出。
栈的应用: 括号匹配问题、逆波兰表达式求值。
5、队列
队列是一种先进先出(FIFO)的数据结构,是一种只能在一端进行插入,另一端进行删除的特殊线性表,按照先入先出的原则存储数据。队列里边有队首,队尾,队首元素。其遵循的原则是先进先出。
队列的核心操作:入队列,出队列,取队首元素。
对于队列的形象理解:火车穿越隧道,火车的头相当于是队列的首,火车的尾相当于是队列的尾部。火车在穿越隧道的时候,头部先进入隧道头部也先出隧道,尾部后进入尾部后出隧道。队列也就是先入的元素先出队列,后进入的元素后出队列。
6、栈和队列的区别
栈和队列的出入方式不同:栈是先进后出、队列是先进先出。
栈和队列在具体实现的时候操作的位置不同:因为栈是先进后出,它在一端进行操作;而队列是先进先出,实现的时候在两端进行。在Java标准库中实现队列时是按照链表实现的。
四、树
1、概念
- 树的定义:树是由(n>=1)个有限节点组成的一个具有层次关系的集合。
- 树的特点
①每个结点具有零个或多个子结点。
②没有父节点的结点为根结点。
③每个非根结点只有一个父结点。
④每个结点及其后代结点整体上可以看作是一棵树,称为当前结点的父结点的一个子树。 - 树的相关术语
结点的度:一个结点 含有的子树的个数 称为该结点的度。
叶子结点:度为0的结点 称为叶结点,也可以叫做终端结点。
分支结点:度不为0的结点 称为分支结点,也可以叫做非终端结点。
结点的层次:从根结点开始,根结点的层次为1,根的直接后继层次为2,以此类推。
结点的层序编号:将树中的结点,按照从上层到下层,同层从左到右的次序排成一个线性序列,把他们编成连续的自然数。
树的度:树中 所有结点的度的最大值。
树的高度(深度):树中 结点的最大层次。
森林:m(m>=0)个互不相交的树的集合,将一颗非空树的根结点删去,树就变成一个森林;给森林增加一个统一的根结点,森林就变成—棵树。
孩子结点:—个结点的直接后继结点称为该结点的孩子结点。
双亲结点(父结点):一个结点的直接前驱称为该结点的双亲结点。
兄弟结点:同一双亲结点的孩子结点间互称兄弟结点。
2、二叉树
二叉树就是 度不超过2的树(每个结点最多有两个子节点)。
一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。
- 二叉树的遍历方式
①前序遍历:先访问根结点,然后再访问左子树,最后访问右子树。
②中序遍历:先访问左子树,中间访问根节点,最后访问右子树。
③后序遍历:先访问左子树,再访问右子树,最后访问根节点。
④层序遍历:从根节点(第一层)开始,依次向下,获取每一层所有结点的值(广度优先遍历)。
3、平衡树
1、2-3查找树
为了保证查找树的平衡性,我们需要一些灵活性,因此在这里我们允许树中的一个结点保存多个键。确切的说,我们将一棵标准的二叉查找树中的结点称为2-结点(含有一个键和两条链),而现在我们引入3-结点,它含有两个键和三条链。2-结点和3-结点中的每条链都对应着其中保存的键所分割产生的一个区间。
-
2-3树满足的条件
2-结点:含有一个键(及其对应的值)和两条链,左链接指向2-3树中的键都小于该结点,右链接指向的2-3树中的键都大于该结点。
3-结点:含有两个键(及其对应的值)和三条链,左链接指向的2-3树中的键都小于该结点,中链接指向的2-3树中的键都位于该结点的两个键之间,右链接指向的2-3树中的键都大于该结点。
-
2-3树的性质
通过对2-3树插入操作的分析,我们发现在插入的时候,2-3树需要做一些局部的变换来保持2-3树的平衡。
—棵完全平衡的2-3树具有以下性质:
①任意空链接到根结点的路径长度都是相等的。
②4-结点变换为3-结点时,树的高度不会发生变化,只有当根结点是临时的4-结点,分解根结点时,树高+1。
③2-3树与普通二叉查找树最大的区别在于,普通的二叉查找树是自顶向下生长,而2-3树是自底向上生长。
2、红黑树
红黑树主要是对2-3树进行编码,红黑树背后的基本思想是用标准的二叉查找树(完全由2-结点构成)和一些额外的信息(替换3-结点)来表示2-3树。我们将树中的链接分为两种类型:
红链接:将两个2-结点连接起来构成一个3-结点。
黑链接:则是2-3树中的普通链接。
确切的说,我们将3-结点表示为由由一条左斜的红色链接(两个2-结点其中之一是另一个的左子结点)相连的两个2-结点。
- 红黑树的定义
红黑树是含有红黑链接并满足下列条件的二叉查找树:
①红链接均为左链接。
②没有任何一个结点同时和两条红链接相连。
③该树是完美黑色平衡的,即任意空链接到根结点的路径上的黑链接数量相同。
一种自平衡的 二叉查找树(左中右↑),除了满足二叉查找树的性质外,还需要满足如下五个条件:
① 每个节点非黑即红;
② 根节点总是黑色的;
③ 每个 叶子 节点都是黑色的 空节点(NIL节点);
④ 如果 节点是红色的,则它的子节点必须是黑色的(反之不一定);
⑤ 从 根节点到叶节点 或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。
应用: TreeMap、TreeSet以及JDK1.8的HashMap底层都用到了红黑树。
为什么要用? 是为了解决二叉查找树在某些情况下会退化成一个线性结构的问题(如:形成二叉查找树的时候插入的元素是有序时,查找性能变成线性)。
- 红黑树平衡化
左旋:当某个结点的左子结点为黑色,右子结点为红色,此时需要左旋。
右旋:当某个结点的左子结点是红色,且左子结点的左子结点也是红色,需要右旋。
4、B树
B树是一种树状数据结构,它能够存储数据、对其进行排序并允许以O(logn)的时间复杂度进行查找、顺序读取、插入和删除等操作。
B树中允许一个结点中包含多个key。M阶的B树,具有如下特点:
①每个结点 最多有M-1个key,并且以升序排列。
②每个结点最多能有M个子结点。
③根结点至少有两个子结点。
5、B+树
B+树是对B树的一种变形树,它与B树的差异在于:
①非叶结点仅具有索引作用,也就是说,非叶子结点只存储key,不存储value。
②树的所有叶结点构成一个有序链表,可以按照key排序的次序遍历全部数据。
- B+树和B树的区别
B+树的优点在于:
①由于B+树在非叶子结点上不包含真正的数据,只当做索引使用,因此在内存相同的情况下,能够存放更多的key。
②B+树的叶子结点都是相连的,因此对整棵树的遍历只需要一次线性遍历叶子结点即可。而且由于数据顺序排列并且相连,所以便于区间查找和搜索。而B树则需要进行每一层的递归遍历。
B树的优点在于:
由于B树的每一个节点都包含key和value,因此我们根据key查找value时,只需要找到key所在的位置,就能找到value,但B+树只有叶子结点存储数据,索引每一次查找,都必须一次一次,一直找到树的最大深度处,也就是叶子结点的深度,才能找到value。
五、堆
堆是计算机科学中一类特殊的数据结构的统称,堆通常可以被看做是 一棵完全二叉树的数组对象。
- 堆的特性
①它是 完全二叉树,除了树的最后一层结点不需要是满的,其它的每一层从左到右都是满的,如果最后一层结点不是满的,那么要求左满右不满。
②通常使用数组来实现。
六、优先队列
最大优先队列: 可以获取并删除队列中最大的值。
最小优先队列: 可以获取并删除队列中最小的值。
七、图
图是由一组顶点和一组能够将两个顶点相连的边组成的。
自环:即一条连接一个顶点和其自身的边。
平行边:连接同一对顶点的两条边。
1、图的分类
按照连接两个顶点的边的不同,可以把图分为以下两种:
无向图: 边仅仅连接两个顶点,没有其他含义。
有向图: 边不仅连接两个顶点,并且具有方向。
2、无向图的相关术语
相邻顶点:当两个顶点通过—条边相连时,我们称这两个顶点是相邻的,并且称这条边依附于这两个顶点。
度:某个顶点的度就是依附于该顶点的边的个数。
子图:是一幅图的所有边的子集(包含这些边依附的顶点)组成的图。
路径:是由边顺序连接的一系列的顶点组成。
环:是—条至少含有一条边且终点和起点相同的路径。
连通图:如果图中任意一个顶点都存在一条路径到达另外一个顶点,那么这幅图就称之为连通图。
连通子图:一个非连通图由若干连通的部分组成,每一个连通的部分都可以称为该图的连通子图。
3、无向图的搜索算法
深度优先搜索和广度优先搜索。
4、有向图的相关术语
定义:有向图是一副具有方向性的图,是由一组顶点和一组有方向的边组成的,每条方向的边都连着一对有序的顶点。
出度:由 某个顶点指出的边的个数 称为该顶点的出度。
入度:指向某个顶点的边的个数 称为该顶点的入度。
有向路径:由—系列顶点组成,对于其中的每个顶点都存在一条有向边,从它指向序列中的下一个顶点。
有向环:—条至少含有一条边,且起点和终点相同的有向路径。