Skip to content

JS实现常见的数据结构 ​

Stack(栈) ​

stack

Stack的特点是后进先出(last in first out)。比如弹夹,后装入的子弹会先发射出去;又比如浏览器的访问历史,当我们点击返回按钮时,最后访问的网站最先从历史记录中弹出。 Stack一般具有以下方法:

  • push:将一个元素推入栈顶
  • pop:移除栈顶元素,并返回被移除的元素
  • peek:返回栈顶元素
  • size:返回栈中元素个数

Javascript的Array天生具备了Stack的特性,下面我们手动实现一个Stack类:

js
class Stack {
  constructor() {
    this.size = 0;
    this.storage = {};
  }
  push(value) {
    this.storage[this.size] = value;
    this.size++;
  }
  pop() {
    if (this.size === 0) {
      return undefined;
    }
    this.size--;
    const result = this.storage[this.size];
    delete this.storage[this.size];
    return result;
  }
  peek() {
    return this.storage[this.size - 1];
  }
}

Queue(队列) ​

queue

Queue和Stack有一些类似,不同的是Stack是先进后出,而Queue是先进先出。Queue最常见的例子就是排队上车,排在第一个的总是最先上车。 Queue一般具有以下方法:

  • enqueue:入列,想队列尾部增加一个元素
  • dequeue:出列,移除队列头部的一个元素并返回被移除的元素
  • front:获取队列的第一个元素
  • isEmpty:判断队列是否为空
  • size:获取队列中元素的个数

Javascript中的Array已经具备Queue的一些特性,下面借助Array实现一个Queue:

js
class Queue {
  constructor() {
    this.collection = [];
    this.size = this.collection.length;
    this.isEmpty = true;
  }
  enqueue(element) {
    this.collection.push(element);
    this.size = this.collection.length;
    this.isEmpty = (this.size === 0);
  }
  dequeue() {
    const result = this.collection.shift();
    this.size = this.collection.length;
    this.isEmpty = (this.size === 0);
    return result;
  }
  front() {
    return this.collection[0];
  }
  print() {
    console.log(this.collection);
  }
}

Priority Queue(优先队列) ​

Queue还有一个升级版本,给每个元素赋予优先级,优先级高的元素入列时排到低优先级之前。区别主要是enqueue方法的实现:

js
class PriorityQueue extends Queue {
  enqueue(element) {
    if (this.isEmpty) {
      this.collection.push(element);
    } else {
      let index = this.collection.findIndex(ele => ele[1] > element[1]);
      if (!~index) {
        this.collection.push(element);
      } else {
        this.collection.splice(index, 0, element)
      }
    }
    this.size = this.collection.length;
    this.isEmpty = (this.size === 0);
  }
}

let pQ = new PriorityQueue();
pQ.enqueue(['gannicus', 3]);
pQ.enqueue(['spartacus', 1]);
pQ.enqueue(['spartacus111', 1]);
pQ.enqueue(['crixus', 2]);
pQ.enqueue(['oenomaus', 4]);

Linked List(链表) ​

linked_list

链表是一种链式数据结构,链表上的每个节点包含两种信息:节点本身的数据和指向下一个节点的指针。链表和传统的数组都是线性的数据结构,存储的都是一个序列的数据,但也有很多区别,如下表

比较维度数组链表
内存分配静态内存分配,编译时分配且连续动态内存分配,运行时分配且不连续
元素获取通过index获取,速度较快通过遍历顺序访问,速度较慢
添加删除元素内存位置连续切固定,速度较慢内存分配灵活,只有一个开销步骤,速度更快
空间结构可以是一维或者多维数组可以是单向、双向或循环链表

一个单向链表通常具有以下方法:

  • size:链表中节点的个数
  • head:链表中的头部元素
  • add:项链表尾部增加一个节点
  • remove:删除某个节点
  • indexOf:返回某个节点的index
  • elementAt:返回某个index处的节点
  • addAt:在某个index处插入一个节点
  • removeAt:删除某个index处的节点
ts
type NodeElement = number | string;

interface INode {
  node: NodeElement;
  next: INode | null;
}

class LinkedNode implements INode {
  node: NodeElement;
  next: INode | null;
  constructor(element: NodeElement) {
    this.node = element;
    this.next = null;
  }
}

interface ILinkedList {
  size: number;
  head: INode | null;
  add: (elemetn: NodeElement) => void;
  addAt: (index: number, elemetn: NodeElement) => void;
  remove: (elemetn: NodeElement) => void;
  removeAt: (index: number) => NodeElement | null;
  indexOf: (elemetn: NodeElement) => number;
  elementAt: (index: number) => NodeElement | null;
  print: () => void;
}

class LinkedList implements ILinkedList {
  size: number;
  head: INode | null;
  constructor() {
    this.size = 0;
    this.head = null;
  }
  add(element: NodeElement) {
    const node = new LinkedNode(element);
    if (this.head === null) {
      this.head = node;
    } else {
      let curNode = this.head;
      while (curNode.next !== null) {
        curNode = curNode.next;
      }
      curNode.next = node;
    }
    this.size++;
  }
  addAt(index: number, element: NodeElement) {
    if (index < 0 || index > this.size) {
      return;
    } else if (index === this.size) {
      this.add(element);
    } else {
      const node = new LinkedNode(element);
      let curNode = this.head;
      if (index === 0) {
        [this.head, this.head!.next] = [node, curNode]
      } else {
        let i = 1;
        let prevNode = curNode;
        curNode = curNode!.next;
        while (i < index) {
          [prevNode, curNode] = [curNode, curNode!.next];
          i++;
        }
        prevNode!.next = node;
        node.next = curNode;
      }
    }
    this.size++;
  }
  remove(element: NodeElement) {
    let prevNode: INode | null = null;
    let curNode = this.head;
    if (curNode?.node === element) {
      this.head = curNode!.next;
      this.size--;
    } else {
      while (curNode !== null && curNode.node !== element) {
        [prevNode, curNode] = [curNode, curNode!.next];
      }
      if (curNode !== null) {
        prevNode!.next = curNode.next;
        this.size--;
      }
    }
  }
  removeAt(index: number) {
    if (index < 0 || index > this.size) {
      return null;
    }
    let curNode = this.head;
    if (index === 0) {
      this.head = curNode!.next;
    } else {
      let i = 1, prevNode = curNode;
      curNode = curNode!.next;
      while (i < index) {
        [prevNode, curNode] = [curNode, curNode!.next]
        i++;
      }
      prevNode!.next = curNode!.next;
    }
    this.size--;
    return curNode!.node;
  }
  indexOf(element: NodeElement) {
    let [curNode, i] = [this.head, 0];
    while (curNode && curNode.node !== element) {
      curNode = curNode.next;
      i++;
    }
    return curNode ? i : -1;
  }
  elementAt(index: number) {
    if (index < 0 || index >= this.size) {
      return null;
    }
    let [curNode, i] = [this.head, 0];
    while (i < index) {
      curNode = curNode!.next;
      i++;
    }
    return curNode?.node ?? null;
  }
  print() {
    let curNode = this.head;
    let LinkedList = [curNode?.node];
    while (curNode?.next) {
      LinkedList.push(curNode.next.node);
      curNode = curNode.next;
    }
    console.log(LinkedList.join('==>'));
  }
}

Set(集合) ​

set

集合是数学中的一个基本概念,表示具有某种特性的对象汇总成的集体。在ES6中也引入了集合类型Set,Set和Array有一定程度的相似,不同的是Set中不允许出现重复的元素而且是无序的。 一个典型的Set应该具有以下方法:

  • values:返回集合中的所有元素
  • size:返回集合中元素的个数
  • has:判断集合中是否存在某个元素
  • add:向集合中添加元素
  • remove:从集合中移除某个元素
  • union:返回两个集合的并集
  • intersection:返回两个集合的交集
  • difference:返回两个集合的差集
  • subset:判断一个集合是否为另一个集合的子集

Hash Table(哈希表/散列表) ​

hash_table

Hash Table是一种用于存储键值对(key value pair)的数据结构,因为Hash Table根据key查询value的速度很快,所以它常用于实现Map、Dictionary、Object等数据结构。如上图所示,Hash Table内部使用一个hash函数将传入的键转换成一串数字,而这串数字将作为键值对实际的key,通过这个key查询对应的value非常快,时间复杂度将达到O(1)。Hash函数要求相同输入对应的输出必须相等,而不同输入对应的输出必须不等,相当于对每对数据打上唯一的指纹。

一个Hash Table通常具有下列方法:

  • add:增加一组键值对
  • remove:删除一组键值对
  • lookup:查找你哥键对应的值