js中的数据结构(一) | 栈、堆与队列

733 阅读3分钟

| 常用数据结构

  1. 数组(Array)
  2. 栈(Stack)先进后出 (LIFO)
  3. 队列(Queue)先进先出(FIFO)
  4. 链表(Linked List)
  5. 树(Tree)
  6. 图(Graph)
  7. 堆(Heap)
  8. 散列表(Hash 哈希表)

如果数据结构中的项目按顺序存储,我们就说数据结构是“线性的”。
数组、链表和堆栈都是线性数据结构。

栈存储数据的方式跟数组一样,都是将元素排成一行。只不过它还有以下3条约束。

  • 只能在末尾插入数据
  • 只能读取末尾的数据
  • 只能移除末尾的数据

队列

你可以将队列想象成是电影院排队。排在最前面的人会最先离队进入影院。
因此计算机科学家都用缩写“FIFO”(first in, first out)先进先出,来形容它。
与栈类似,队列也有3个限制(但内容不同)。

  • 只能在末尾插入数据(这跟栈一样)
  • 只能读取开头的数据(这跟栈相反)
  • 只能移除开头的数据(这也跟栈相反)

| js中的数据结构

javaScript中,数据类型分为基本数据类型引用数据类型

栈内存

基本数据类型七种包含:null、undefined、string、number、boolean、symbol、bigint
这七种数据类型存储在栈空间,我们按值访问。
基本类型都存储在栈内存中,是大小固定并且有序的

栈很少用于需要长期保留数据的场景,却常用于各种处理临时数据的算法。
计算机是用栈来记录每个调用中的函数。这个栈就叫作调用栈。

堆内存

一般由操作人员(程序员)分配释放,若操作人员不分配释放,将由OS(操作系统)回收释放。
分配方式类似链表。

堆存储在二级缓存中。

JavaScript 使用堆内存的 Object 类型,它包含:

  • Object
  • Function
  • Array
  • Date
  • RegExp

栈和堆的区别

  1. 空间分配不同

栈由系统自动分配释放堆由程序员分配释放

  1. 缓存方式不同
    栈是一级缓存,存取速度快,不灵活
    堆是二级缓存,存取速度慢,较灵活

实现

  1. js实现入栈和出栈
let stackArr = []
// 入栈
stackArr.push(something)
// 出栈
stackArr.pop()
😂😂
  1. js实现队列
let queueArr = []
// 入列
queueArr.push(something)
// 出列
queueArr.shift()
🙈

栈的应用

递归
递归十分适用于那些无法预估计算深度的问题,如对象的深拷贝文件系统遍历
改用递归并不会改变算法的大O

递归之于阶乘

const factorial = (num)=> {
  if (num <= 1) return num;
  return factorial(num - 1) * num;
}

递归之于快排
快速排序(Quicksort),又称划分交换排序(partition-exchange sort),简称快排,是一种效率很高的排序算法

快速排序是不稳定的排序方法
当数组是完全无序时, 时间复杂度最低,为O(nlog2n)
当数组完全有序时,时间复杂度最高,为O(n^2)
快速排序严重依赖于分区

利用递归实现快排

  1. 拿出一个中间数来分区
  2. 遍历判断值在左或右并存入数组
  3. 继续递归左右数组并合并,记得合并中间数
  4. 返回合并结果
const quickSort = arr => {
    if (arr.length <= 1) return arr;
    
    let center = arr.shift(), // 拿出一个中间数
        leftArr = [],
        rightArr = [],
        result = [];

    // 遍历判断值在左或右并存入数组
    for (let i = 0;i <=arr.length; i++) { 
        arr[i] <= center && leftArr.push(arr[i]);
        arr[i] > center && rightArr.push(arr[i]);   
    }
    
    result = quickSort(leftArr).concat(center, quickSort(rightArr)); // 继续递归左右数组并合并,记得合并中间数
    return result;        
}

队列的应用

队列应用广泛,从打印机的作业设置,到网络应用程序的后台任务,都有队列的存在。
队列也是处理异步请求的理想工具——它能保证请求按接收的顺序来执行,如event队列,promise回调队列

使用队列计算斐波那契数列的第n项

  1. 斐波那契数列的前两项固定为1
  2. 后面的项为前两项之和
  3. 依次向后,这便是斐波那契数列
const fibonacci = (n) => {
    const queue = [];
    queue.push(1);
    queue.push(1);
    
    let index = 0;
    while(index < n - 2) {
        index += 1;
        // 出队列一个元素
        const delItem = queue.shift();
        // 获取头部值
        const headItem = queue[0];
        const nextItem = delItem + headItem;
        queue.push(nextItem);
    }
    
    return queue[queue.length - 1];
}
console.log(fibonacci(9)); // 34

| 参考

JS数据结构与算法_栈&队列
数据结构与算法图解