2020-06-11 数据结构基础

物理结构

这些是在内存中实实在在的存储结构。

1.数组

数组是有限个相同类型变量的有序集合,其中的每一个变量被称为元素。

数组在内存中顺序存储,因此可以很好的在逻辑上实现顺序表。

数组的基本操作
  1. 读取元素 ,如array[2]
  2. 更新元素,如array[3] = 2
  3. 插入元素,如果数组未满插入那么插入位置至尾部元素都后移,然后再插入到相应位置;如果数组已满插入,即超范围插入,那么数组要先扩容再插入,时间复杂度O(n)
  4. 删除元素,删除后元素前移,时间复杂度O(n)
数组的优劣

由于数组的特点,其有高效的随机访问能力,但是其插入和删除操作都会导致大量元素被迫移动影响效率,所以其适合读操作多,写操作少的场景。

2.链表

链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干节点(node)组成。每一个节点都有存储数据的变量data和指向下一个节点的指针next。

链表有单向链表和双向链表,双向链表有一个这项前置节点的指针prev。

链表在内存中是随机存储的。

链表的基本操作
  1. 查找节点,只能从头节点开始一个一个向后查找,不能像数组一样随机查找
  2. 更新节点,与数组一样更新数据,前提是查找到那个数据
  3. 插入节点,尾部和头部插入只需要更改一个指针,中间插入需要更改自身和前一个节点的指针
  4. 删除元素,同样需要更改指针即可,弃用的节点如果没有引用,在高级语言里自带的垃圾回收机制会自行回收
链表的优劣

链表与数组的比较

查找 更新 插入 删除
数组 O(1) O(1) O(n) O(n)
链表 O(n) O(1) O(1) O(1)

注意这里链表的更新和插入以及删除都没有包括查找过程。

当然如果直接在链表尾部进行插入和删除其时间复杂度就是O(1),所以相比于数组,链表更加适合需要在尾部频繁使用插入、删除操作的场景

逻辑结构

逻辑结构是抽象的概念,它依赖于物理结构而存在。

1.栈

栈(stack)是一种线性数据结构,栈中的元素只能先入后出(First In Last Out,简称FILO)。最早进入的元素存放的位置叫做栈底(bottom),最后进入的元素存放的位置叫做栈顶(top)。

栈既可以使用数组来实现,也可以使用链表来实现。

栈的基本操作
  1. 入栈(push),只允许从栈的一侧放入元素
  2. 出栈(pop),这两个操作时间复杂度都是O(1)
2.队列

队列(queue)是一种线性数据结构,队列中的元素只能先入先出(First In First Out,简称FIFO)。队列的出端口叫做队头(front),队列的入端口叫做队尾(rear)。

队列也是既可以使用数组也可以使用链表来实现。

数组实现的时候,为了入队操作方便,把队尾位置规定为最后入队元素的下一个位置。

队列的基本操作
  1. 入队(enqueue),只允许在队尾位置放入元素,新元素的下一个位置将成为队尾。
  2. 出队(dequeue),只允许在队头一侧移出元素,出队元素的后一个元素将成为新的队头。

但是这样队列出队操作多的时候就会在队头空出空间,可以采取循环队列的方法来实现空间的合理利用。

3.栈和队列的应用
  1. 栈的应用,栈输入和输出顺序相反,所以栈通常用于对历史的回溯,如递归的实现、浏览器回到上一级页面等。
  2. 队列的应用,队列输入和输出顺序相同,所以队列常用于对历史的回放,按照历史顺序重演一遍。如爬虫中把待抓取的网页存放在队列中。
  3. 双端队列,结合了栈和队列的优点,两端都可以入队和出队。
  4. 优先队列,谁的优先级高谁先出队,它是基于二叉堆来实现的。
4.散列表

散列表也叫作哈希表(Hash Table),这种数据结构提供了键(Key)和值(Value)的映射关系。给出一个Key,就可以高效的查找所匹配的Value,时间复杂度近似于(O(1))。

散列表通过哈希函数将键值进行一些运算,得到值存储的位置实现一一对应。

写操作(put)

在散列表中插入新的键值对,注意如果散列表中的同键指向同一存储位置,这是哈希冲突是会发生的,如何解决呢?主要有两种方法,一是开放寻址法、一是链表法。

开放寻址法主要是如果要插入的位置已经有数据了,那么就寻找下一个空档,找到没有被占用的位置然后插入,比如冲突时就后移一个位置知道找到空位。

链表法使用的时候存储数据的数组的每个元素都是一个链表的头结点,这样插入到对应的链表即可。

读操作(get)

就是通过给定的key在散列表中找到对应的value返回即可。

扩容(resize)

散列表基于数组实现,什么时候需要扩容呢?

当散列表达到一定饱和度的时候,key映射位置发生冲突的概率会逐渐提高,大量元素拥挤在同一位置周围,这时就需要扩容。散列表的容量是影响扩容的重要因素。

扩容经历了如下步骤:

  1. 扩容,创建一个新的数组,长度是原来的两倍。
  2. 重新Hash,遍历原来的数组,把所有元素重新Hash到新数组中,注意经过扩容Hash的规则也随之改变所以要重新Hash。

你可能感兴趣的:(2020-06-11 数据结构基础)