数据结构基础

数组

数组的另一个特点,是在内存中顺序存储,因此可以很好地实现逻辑上的顺序 表。

数组在内存中的顺序存储,具体是什么样子呢?

内存是由一个个连续的内存单元组成的,每一个内存单元都有自己的地址。在这些内存单元中,有些被其他数据占用了,有些是空闲的。

数组中的每一个元素,都存储在小小的内存单元中,并且元素之间紧密排列, 既不能打乱元素的存储顺序,也不能跳过某个存储单元进行存储。

数组的插入和删除操作,时间复杂度分别是多少?
先说说插入操作,数组扩容的时间复杂度是O(n),插入并移动元素的时间复杂度也是O(n),综合起来插入操作的时间复杂度是O(n)。至于删除操作,只涉及元素的移动,时间复杂度也是O(n)。

数组的优势和劣势?
数组拥有非常高效的随机访问能力,只要给出下标,就 可以用常量时间找到对应元素。有一种高效查找元素的算法叫作二分查找, 就是利用了数组的这个优势。

至于数组的劣势,体现在插入和删除元素方面。由于数组元素连续紧密地存储在内存中,插入、删除元素都会导致大量元素被迫移动,影响效率。

数组所适合的是读操作多、写操作少的场景

链表

单向链表

在这里插入图片描述
链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。

单向链表的每一个节点又包含两部分,一部分是存放数据的变量data,另一部 分是指向下一个节点的指针next。

链表的第1个节点被称为头节点,最后1个节点被称为尾节点,尾节点的next指 针指向空。

与数组按照下标来随机寻找元素不同,对于链表的其中一个节点A,我们只能根 据节点A的next指针来找到该节点的下一个节点B,再根据节点B的next指针找到下 一个节点C……

双向链表

双向链表比单向链表稍微复杂一些,它的每一个节点除了拥有data和next指 针,还拥有指向前置节点的prev指针。
在这里插入图片描述
如果说数组在内存中的存储方式是顺序存储,那么链表在内存中的存储方式则是随机存储。

什么叫随机存储呢?
数组的内存分配方式,数组在内存中占用了连续完整的存储空间。而链表则采用了见缝插针的方式,链表的每一个节点分布在内存的不同位置,依靠next指针关联起来。
在这里插入图片描述
图中的箭头代表链表节点的next指针。

链表的插入和删除操作,时间复杂度分别是多少?

如果不考虑插入、删除操作之前查找元素的过程,只考虑纯粹的插入和删除操作,时间复杂度都是O(1)。

数组VS链表

在这里插入图片描述
数组的优势在于能够快速定位元素,对于读操作多、写操作少的场景来说,用数组更合适一些。
链表的优势在于能够灵活地进行插入和删除操作,如果需要在尾部频繁插入、删除元素,用链表更合适一些。

在这里插入图片描述
栈(stack)是一种线性数据结构,它就像一个上图所示的放入乒乓球的圆筒容器,栈中的元素只能先进后出(First In Last Out,简称FILO)。最早进入的元素存放的位置叫作栈底(bottom),最后进入的元素存放的位置叫作栈顶(top)。

栈这种数据结构既可以用数组来实现,也可以用链表来实现。

栈的数组实现如下
在这里插入图片描述

栈的链表实现如下
在这里插入图片描述

入栈操作

入栈操作(push)就是把新元素放入栈中,只允许从栈顶一侧放入元素,新元素的位置将会成为新的栈顶。
在这里插入图片描述

出栈操作

出栈操作(pop)就是把元素从栈中弹出,只有栈顶元素才允许出栈,出栈元素的前一个元素将会成为新的栈顶。
在这里插入图片描述

队列

在这里插入图片描述

队列中的元素只能先进先出(First In First Out,简称FIFO)。队列的出口端叫作队头(front),队列的入口端叫作队尾(rear)。

与栈类似,队列这种数据结构既可以用数组来实现,也可以用链表来实现。
用数组实现时,为了入队操作的方便,把队尾位置规定为最后入队元素的下一个位置。

队列的数组实现如下
在这里插入图片描述
队列的链表实现如下
在这里插入图片描述

入队

入队(enqueue)就是把新元素放入队列中,只允许在队尾的位置放入元素,新元素的下一个位置将会成为新的队尾。
在这里插入图片描述

出队

出队操作(dequeue)就是把元素移出队列,只允许在队头一侧移出元素,出队元素的后一个元素将会成为新的队头。
在这里插入图片描述

散列表

散列表也叫作哈希表(hash table),这种数据结构提供了键(Key)和值 (Value)的映射关系。只要给出一个Key,就可以高效查找到它所匹配的Value,时 间复杂度接近于O(1)。
在这里插入图片描述
哈希函数

  • 通过某种方式,把Key和 数组下标进行转换。这个中转站就叫作哈希函数。通过哈希函数,我们可以把字符串或其他类型的Key,转化成数组的下标 index。

在这里插入图片描述

散列表的读写操作

写操作

写操作(put)写操作就是在散列表中插入新的键值对(在JDK中叫作Entry)。

如调用hashMap.put(“002931”,“王五”),意思是插入一组Key为002931、Value为王五的键值对。具体该怎么做呢?

  • 第1步,通过哈希函数,把Key转化成数组下标5。
  • 第2步,如果数组下标5对应的位置没有元素,就把这个Entry填充到数组下标5
    在这里插入图片描述
    但是,由于数组的长度是有限的,当插入的Entry越来越多时,不同的Key通过 哈希函数获得的下标有可能是相同的。例如002936这个Key对应的数组下标是2; 002947这个Key对应的数组下标也是2。
    在这里插入图片描述
    这种情况,就叫作哈希冲突。

解决哈希冲突的方法主要有两种,一种是开放寻址法,一种是链表法。

解决哈希冲突 - 开放寻址法

开放寻址法: 开放寻址法的原理很简单,当一个Key通过哈希函数获得对应的数组下标已被占 用时,我们可以“另谋高就”,寻找下一个空档位置。(在Java中,ThreadLocal所使用的就是开放寻址法。)

解决哈希冲突 - 链表法

当新来的Entry映射到与之冲突的数组位置时,只需要插入到对应的链表中即可。
在这里插入图片描述

读操作

例如调用 hashMap.get(“002936”),意思是查找Key为002936的Entry在散列 表中所对应的值。 具体该怎么做呢?下面以链表法为例来讲一下。

  • 第1步,通过哈希函数,把Key转化成数组下标2。
  • 第2步,找到数组下标2所对应的元素,如果这个元素的Key是002936,那么就 找到了;如果这个Key不是002936也没关系,由于数组的每个元素都与一个链表对应,我们可以顺着链表慢慢往下找,看看能否找到与Key相匹配的节点。
    在这里插入图片描述
    在上图中,首先查到的节点Entry6的Key是002947,和待查找的Key 002936不 符。接着定位到链表下一个节点Entry1,发现Entry1的Key 002936正是我们要寻 找的,所以返回Entry1的Value即可。

扩容(resize)

当经过多次元素插入,散列表达到一定饱和度时,Key映射位置发生冲突的概率 会逐渐提高。这样一来,大量元素拥挤在相同的数组下标位置,形成很长的链表, 对后续插入操作和查询操作的性能都有很大影响。
在这里插入图片描述

对于JDK中的散列表实现类HashMap来说,其扩容

  • 1.扩容,创建一个新的Entry空数组,长度是原数组的2倍。
  • 2.重新Hash,遍历原Entry数组,把所有的Entry重新Hash到新数组中。为什 么要重新Hash呢?因为长度扩大以后,Hash的规则也随之改变。

关于HashMap的实现,JDK 8和以前的版本有着很大的不同。当 多个Entry被Hash到同一个数组下标位置时,为了提升插入和查找的效率,HashMap 会把Entry的链表转化为红黑树这种数据结构。建议读者把两个版本的实现都认真地 看一看,这会让你受益匪浅。

二叉树

二叉树(binary tree)是树的一种特殊形式。二叉,顾名思义,这种树的每 个节点最多有2个孩子节点。注意,这里是最多有2个,也可能只有1个,或者没有孩 子节点。

满二叉树

一个二叉树的所有非叶子节点都存在左右孩子,并且所有叶子节点都在同一层 级上,那么这个树就是满二叉树。
在这里插入图片描述

完全二叉树

在这里插入图片描述

满二叉树要求所有分支都是满的; 而完全二叉树只需保证最后一个节点之前的节点都齐全即可。

二叉查找树

二叉查找树要求左子树小于父节点,右子树 大于父节点,正是这样保证了二叉树的有序性

在这里插入图片描述

二叉堆

二叉堆本质上是一种完全二叉树,它分为两个类型: 最大堆、最小堆。

  • 最大堆
    最大堆的任何一个父节点的值,都大于或等于它左、右孩子 节点的值。
    在这里插入图片描述
  • 最小堆
    最小堆的任何一个父节点的值,都小于或等于它左、右孩子 节点的值。
    在这里插入图片描述
    最大堆和最小堆的特点决定了:最大堆的堆顶是整个堆中的最大元素;最小堆 的堆顶是整个堆中的最小元素。

优先队列

优先队列不再遵循先入先出的原则,而是分为两种情况。

  • 最大优先队列,无论入队顺序如何,都是当前最大的元素优先出队
  • 最小优先队列,无论入队顺序如何,都是当前最小的元素优先出队
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值