Java数据结构知识点 — 5种常见数据结构

一. 数据结构

1.线性数据结构

(1)数组

数组(Array) 是一种很常见的数据结构。它由相同类型的元素(element)组成,并且是使用一块连续的内存来存储。可以利用元素的索引(index)计算出该元素对应的存储地址。

数组的特点是:提供随机访问 并且容量有限。

假如数组的长度为 n。
访问:O(1)//访问特定位置的元素
插入:O(n )//最坏的情况发生在插入发生在数组的首部并需要移动所有元素时
删除:O(n)//最坏的情况发生在删除数组的开头发生并需要移动第一元素后面所有的元素时

 (2)链表

链表(LinkedList) 虽然是一种线性表,但是并不会按线性的顺序存储数据,使用的不是连续的内存空间来存储数据。

链表的插入和删除操作的复杂度为 O(1) ,只需要知道目标位置元素的上一个元素即可。但是,在查找一个节点或者访问特定位置的节点的时候复杂度为 O(n) 。

使用链表结构可以克服数组需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但链表不会节省空间,相比于数组会占用更多的空间,因为链表中每个节点存放的还有指向其他节点的指针。除此之外,链表不具有数组随机读取的优点。

常见链表分类有:单链表、双向链表、循环链表、双向循环链表。

假如链表中有n个元素。
访问:O(n)//访问特定位置的元素
插入删除:O(1)//必须要要知道插入元素的位置
  • 单链表

单向链表只有一个方向,结点只有一个后继指针 next 指向后面的节点。因此,链表这种数据结构通常在物理内存上是不连续的。我们习惯性地把第一个结点叫作头结点,链表通常有一个不保存任何值的 head 节点(头结点),通过头结点我们可以遍历整个链表。尾结点通常指向 null。

  • 双向链表

双向链表 包含两个指针,一个 prev 指向前一个节点,一个 next 指向后一个节点。

  • 循环链表 

循环链表 其实是一种特殊的单链表,和单链表不同的是循环链表的尾结点不是指向 null,而是指向链表的头结点。

  • 双向循环链表

双向循环链表 最后一个节点的 next 指向 head,而 head 的 prev 指向最后一个节点,构成一个环。

应用场景:

链表适合存储的数据元素个数不确定,并且经常需要进行添加删除操作,链表不支持随机访问。

数组适合存储的数据元素个数确定,并且不经常需要添加删除操作。

链表和数组比较:

  • 数组支持随机访问,而链表不支持。
  • 数组使用的是连续内存空间对 CPU 的缓存机制友好,链表则相反。
  • 数组的大小固定,而链表则天然支持动态扩容。如果声明的数组过小,需要另外申请一个更大的内存空间存放数组元素,然后将原数组拷贝进去,这个操作是比较耗时的。

(3)栈

栈 (Stack) 只允许在有序的线性数据集合的一端(称为栈顶 top)进行加入数据(push)和移除数据(pop)。因而按照 后进先出(LIFO, Last In First Out) 的原理运作。在栈中,push 和 pop 的操作都发生在栈顶。

栈常用一维数组或链表来实现,用数组实现的栈叫作 顺序栈 ,用链表实现的栈叫作 链式栈 。

假设堆栈中有n个元素。
访问:O(n)//最坏情况
插入删除:O(1)//顶端插入和删除元素

(4)队列

队列(Queue)先进先出 (FIFO,First In, First Out) 的线性表。在具体应用中通常用链表或者数组来实现,用数组实现的队列叫作 顺序队列 ,用链表实现的队列叫作 链式队列队列只允许在后端(rear)进行插入操作也就是入队 enqueue,在前端(front)进行删除操作也就是出队 dequeue

队列的操作方式和堆栈类似,唯一的区别在于队列只允许新数据在后端进行添加。

假设队列中有n个元素。
访问:O(n)//最坏情况
插入删除:O(1)//后端插入前端删除元素

队列分类:单队列、双端队列、循环队列、优先级队列。

  • 单队列

单队列每次添加元素时都是添加到队尾。它又分为顺序队列(数组实现) 和 链式队列(链表实现)顺序队列存在“假溢出”的问题,也就是明明有位置却不能添加的情况。

  • 双端队列

双端队列 (Deque) 是一种在队列的两端都可以进行插入和删除操作的队列,相比单队列来说更加灵活。

  • 循环队列

循环队列可以解决顺序队列的假溢出和越界问题。解决办法就是:从头开始,这样也就会形成头尾相接的循环。

将 rear 指针指向数组下标为 0 的位置就不会有越界问题了。当我们再向队列中添加元素的时候, rear 向后移动。

顺序队列中,我们说 front == rear 的时候队列为空,循环队列中则不一样,也可能为满,如上图所示。解决办法有两种:

  1. 可以设置一个标志变量flag,当 front == rear 并且flag=0的时候队列为空,当 front == rear 并且flag=1的时候队列为满。
  2. 队列为空的时候就是 front == rear ,队列满的时候,我们保证数组还有一个空闲的位置,rear 就指向这个空闲位置,那么现在判断队列是否为满的条件就是:(rear+1)%QueueSize == front。
  • 优先队列

优先队列从底层来看是由堆实现的。

  1. 在每个元素入队时,优先队列会将新元素其插入堆中并调整堆。
  2. 在队头出队时,优先队列会返回堆顶元素并调整堆。

优先队列都能按照某种排序方式进行一系列堆的相关操作,从而保证整个集合的有序性

应用场景:

阻塞队列、任务队列等。

2.图

图是较为复杂的非线性结构,图形结构的元素之间的关系是任意的。

图就是由顶点的有穷非空集合和顶点之间的边组成的集合。通常表示为:G(V,E),其中,G 表示一个图,V 表示顶点的集合,E 表示边的集合。

顶点:图中的数据元素,图至少有一个顶点(非空又穷集合)。

边:顶点之间的关系用边表示。

度:度表示一个顶点包含多少条边,在有向图中,还分为出度和入度,出度表示从该顶点出去的边的条数,入度表示进入该顶点的边的条数。

有向图与无向图:有向图是指关系具有单一性,无向图是双向关系。

无权图和带权图:强调关系之间的强度。

图的存储:

邻接矩阵存储

 如果顶点i和j之间有关系,并且权重为n,那么可表示为A[i][j]=n。

在无向图中,顶点i和j有关系,则表示为A[i][j]=1,没关系为A[i][j]=0。

邻接矩阵存储的方式优点是简单直接(直接使用一个二维数组即可),并且在获取两个定点之间的关系的时候也非常高效(直接获取指定位置的数组元素的值即可)。但是,这种存储方式的缺点也比较明显,那就是比较浪费空间。

邻接表存储:

邻接链表使用一个链表来存储某个顶点的所有后继相邻顶点。对于图中每个顶点 Vi,把所有邻接于 Vi 的顶点 Vj 链成一个单链表,这个单链表称为顶点 Vi 的 邻接表

在无向图中,邻接表元素个数等于边的条数的两倍。在有向图中,邻接表元素个数等于边的条数。

图的搜索: 

广度优先搜索:基于队列实现。

深度优先搜索:基于栈实现。

3.树

一棵树具有以下特点:

  1. 一棵树中的任意两个结点有且仅有唯一的一条路径连通。
  2. 一棵树如果有 n 个结点,那么它一定恰好有 n-1 条边。
  3. 一棵树不包含回路。

任何一个非空树都只有一个根节点。

  • 节点:树中的每个元素都可以统称为节点。
  • 根节点:顶层节点或者说没有父节点的节点。上图中 A 节点就是根节点。
  • 父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点。上图中的 B 节点是 D 节点、E 节点的父节点。
  • 子节点:一个节点含有的子树的根节点称为该节点的子节点。上图中 D 节点、E 节点是 B 节点的子节点。
  • 兄弟节点:具有相同父节点的节点互称为兄弟节点。上图中 D 节点、E 节点的共同父节点是 B 节点,故 D 和 E 为兄弟节点。
  • 叶子节点:没有子节点的节点。上图中的 D、F、H、I 都是叶子节点。
  • 节点的高度:该节点到叶子节点的最长路径所包含的边数。
  • 节点的深度:根节点到该节点的路径所包含的边数
  • 节点的层数:节点的深度+1。
  • 树的高度:根节点的高度

二叉树:

二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于 2 的节点)的树结构。

二叉树 的分支通常被称作“左子树”或“右子树”。并且,二叉树 的分支具有左右次序,不能随意颠倒。二叉树第i层至多拥有2^(i-1)个节点,深度为k的二叉树至多拥有2^(k+1)-1个节点(满二叉树),至少拥有2^(k)个节点。

满二叉树:

每一层节点数都达到最大值,如果一个二叉树层数为k,节点总数为(2^k)-1,那么它就是满二叉树。

完全二叉树: 

除最后一层外,若其余层都是满的,并且最后一层或者是满的,或者是在右边缺少连续若干节点,则这个二叉树就是 完全二叉树 。

平衡二叉树:

平衡二叉树是一棵二叉排序树。

它可以是一棵空树,如果不是空树,那么左右两个子树高度差的绝对值不超过1,并且左右两个子树都是平衡二叉树。

二叉树的存储:链式存储和顺序存储

链式存储

二叉树的链式存储依靠指针将各个节点串联起来,不需要连续的存储空间。

顺序存储

顺序存储就是利用数组进行存储,数组中的每一个位置仅存储节点的 data,不存储左右子节点的指针,子节点的索引通过数组下标完成。根结点的序号为 1,对于每个节点 Node,假设它存储在数组中下标为 i 的位置,那么它的左子节点就存储在 2i 的位置,它的右子节点存储在下标为 2i+1 的位置。

如果要存储的二叉树不是完全二叉树,在数组中就会出现空隙,导致内存利用率降低。

二叉树的遍历

先序遍历:根—左—右。

中序遍历:左—根—右。

后序遍历:左—右—根。

4.堆

堆是一种满足以下条件的树:

堆中的每一个节点值都大于等于(或小于等于)子树中所有节点的值。或者说,任意一个节点的值都大于等于(或小于等于)所有子节点的值。

堆分为最大堆(1)和最小堆(2):

堆的存储:

为了方便存储和索引,(二叉)堆可以用完全二叉树的形式进行存储。

堆的操作:

堆的更新操作主要包括两种 : 插入元素 和 删除堆顶元素。 

 插入元素:

  1. 将要插入的元素放到最后。
  2. 从底向上,如果父结点比该元素小,则该节点和父结点交换,直到无法交换。

删除元素:

根据堆的性质可知,最大堆的堆顶元素为所有元素中最大的,最小堆的堆顶元素是所有元素中最小的。当我们需要多次查找最大元素或者最小元素的时候,可以利用堆来实现。

删除堆顶元素后,为了保持堆的性质,需要对堆的结构进行调整,我们将这个过程称之为"堆化",堆化的方法分为两种:

  • 一种是自底向上的堆化,上述的插入元素所使用的就是自底向上的堆化,元素从最底部向上移动。
  • 另一种是自顶向下堆化,元素由最顶部向下移动。

自底向上堆化:

 自顶向下堆化:

首先要将最后一个元素移动到堆顶。

  • 插入元素:先将元素放至数组末尾,再自底向上堆化,将末尾元素上浮
  • 删除堆顶元素:删除堆顶元素,将末尾元素放至堆顶,再自顶向下堆化,将堆顶元素下沉。也可以自底向上堆化,只是会产生“气泡”,浪费存储空间。最好采用自顶向下堆化的方式。

堆排序:

  • 第一步是建堆,将一个无序的数组建立为一个堆。
  • 第二步是排序,将堆顶元素取出,然后对剩下的元素进行堆化,反复迭代,直到所有元素被取出为止。

建堆是将非叶节点自顶向下堆化的过程。如果节点个数为 n,那么我们需要对 n/2 到 1 的节点进行自顶向下(沉底)堆化。

将初始的无序数组抽象为一棵树,图中的节点个数为 6,所以 4,5,6 节点为叶节点,1,2,3 节点为非叶节点,所以要对 1-3 号节点进行自顶向下(沉底)堆化,注意,顺序是从后往前堆化,从 3 号节点开始,一直到 1 号节点。

 排序

由于堆顶元素是所有元素中最大的,所以我们重复取出堆顶元素,将这个最大的堆顶元素放至数组末尾,并对剩下的元素进行堆化即可。

 5.布隆过滤器

由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构。它占用空间更少并且效率更高,但是缺点是其返回的结果是概率性的,而不是非常准确的。理论情况下添加到集合中的元素越多,误报的可能性就越大。并且,存放在布隆过滤器的数据不容易删除。

布隆过滤器原理:

当一个元素加入布隆过滤器中的时候,会进行如下操作:

  1. 使用布隆过滤器中的哈希函数对元素值进行计算,得到哈希值(有几个哈希函数得到几个哈希值)。
  2. 根据得到的哈希值,在位数组中把对应下标的值置为 1。

当我们需要判断一个元素是否存在于布隆过滤器的时候,会进行如下操作:

  1. 对给定元素再次进行相同的哈希计算;
  2. 得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。

不同的字符串可能哈希出来的位置相同,这种情况可以适当增加位数组大小或者调整哈希函数。

布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。

使用场景:

  • 判断给定数据是否存在:从海量数据中判断是否存在;邮箱的垃圾邮件;黑名单功能等。
  • 去重。

主要还是为了解决海量数据的存在性问题。

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Phoenixxxxxxxxxxxxx

感谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值