数据结构基础
关键词:数组
链表
栈
队列
哈希表
数据结构的操作无非是增删改查4种情况
数组
数组读取元素和更新元素的时间复杂度都是O(1)
插入元素
插入数组元素的操作存在3种情况
尾部插入:直接把插入的元素放在数组尾部的空闲位置即可,等同于更新元素的操作
中间插入:首先把插入位置及后面的元素向后移动,再把要插入的元素放对应的数组位置上
function insert(ele,arr,size){
let length = arr.length
if(size < 0 || size > arr.length){
throw new Error('超出范围限制!')
}
for(let i = length - 1; i >= size; i--){
arr[i + 1] = arr[i]
}
arr[size] = ele
}
超范围插入:先扩容,具体操作是创建一个新数组,长度是旧数组的2倍,再把旧数组中的元素全部复制到新数组
删除元素
如果删除的元素位于数组中间,那么其后的元素都需要向前挪一位
for(let i = index ; i < length - 1; i++){
arr[i] = arr[i + 1]
}
数组插入和删除操作的时间复杂度都是O(n)
链表
链表是一种物理上非连续,非顺序的数据结构,由若干节点(node)所组成
单向链表
单向链表的每一个节点又包含两部分,一部分是存放数据的变量data,另一部分是指向下一个节点的指针next
链表的第一个节点被称为头节点,最后一个节点被称为尾节点,尾节点的next指针指向空,与数组按照下标来随机寻找元素不同,对于链表的其中一个节点A,我们只能根据节点A的next指针来找到该节点的下一个节点B,再根据节点B的next指针找到下一个节点C…
双向链表
双向链表的每一个节点除了拥有data和next指针,还拥有指向前置节点的prev指针,如果说数组在内存中的存储方式是顺序存储,那么链表在内存中的存储方式则是随机存储
查找节点
链表只能从头节点开始向后一个一个节点逐一查找
第一步,将查找的指针定位到头节点
第二步,根据头节点的next指针,定位到第2个节点,依次查找
由于链表中的数据只能按顺序进行访问,最坏的时间复杂度是O(n)
插入节点
尾部插入:把最后一个节点的next指针指向新插入的节点即可
头部插入
第一步,把新节点的next指针指向原来的头节点
第二步,把新节点变为链表的头节点
中间插入
第一步,新节点的next指针,指向插入位置的节点
第二步,插入位置前置节点的next指针,指向新节点
删除元素
尾部删除:把倒数第2个节点的next指针指向空即可
头部删除:把链表的头节点设为原先头节点的next指针即可
中间删除:把要删除节点的前置节点的next指针指向要删除元素的下一个节点即可
如果不考虑插入,删除操作之前查找元素的过程,只考虑纯粹的插入和删除操作,时间复杂度都是O(1)
数组和链表都属于线性的数据结构,对于读操作多,写操作少的场景来说,用数组更合适一些,如果需要在尾部频繁插入,删除元素,用链表更合适一些
栈
物理结构和逻辑结构
物理结构
常用的数据结构有很多,但是大部分都以数组或链表作为作为存储方式,数组和链表可以被看作数据存储的“物理结构”
逻辑结构
如果把物质层面的人体比作数据存储的物理结构,那么精神层面的人格则是数据存储的逻辑结构·。逻辑结构是抽象的概念,它依赖于物理结构而存在
栈是一种线性数据结构,栈中的元素只能先入后出,最早进入的元素存放的位置叫做栈底,最后进入的元素存放的位置叫做栈顶。栈即可以用数组实现,也可以用链表实现
入栈和出栈只会影响到最后一个元素,不涉及其他元素的整体移动,所以无论是以数组还是以链表实现,入栈出栈的时间复杂度都是O(1)
栈通常用于实现递归的逻辑
队列
队列中的元素只能先入先出,队列的出口端叫作队头,队列的入口端叫做队尾
用数组实现时,为了入队操作的方便,把队尾位置规定为最后入队元素的下一个位置
入队就是把新元素放入队列中,只允许在队尾的位置放入元素,出队就是把元素移出队列,只允许在队头一侧移出元素
循环队列
假设一个队列经过反复的入队和出队操作,还剩下两个元素,在"物理上"的末尾位置,这时又有一个新元素将要入队;在数组不扩容的前提下,可以利用已出队元素留下的空间,让队尾指针重新指回数组的首位,这样就实现了一个循环队列。在物理存储上,队尾的位置也可以在队头前。当再有元素入队时,将其放入数组的首位,队尾指针继续后移即可;一直到(队尾下标+1)%数组长度 = 队头下标
时,代表此队列真的已经满了。需要注意的是,队尾指针指向的位置永远空出一位,所以队列最大容量比数组长度小1。循环队列入队出队的时间复杂度同样是O(1)
双端队列
即把栈和队列的特点的结合起来,既可以先入先出,也可以先入后出
优先队列
遵循谁的优先级最高,谁先出队
散列表
散列表也叫哈希表,这种数据结构提供了键(key)和值(value)的映射关系。只要给出一个key,就可以高效查找它所匹配的Value,时间复杂度为O(1),散列表的本质是数组
哈希函数
通过某种方式把key和数组下标进行转换,这种中转站叫做哈希函数
哈希冲突
不同的key通过哈希函数获得的下标有可能是相同的,哈希冲突的解决解决方法一种是开放寻址法,一种是链表法
散列表的扩容(扩展长度)
第一步:创建一个新的Entry空数组,长度是原数组的2倍
第二步:重新Hash,遍历原Entry数组,把所有的Entry重新Hash到新数组中