通过 JavaScript 探索堆栈和队列

非常常见,完全解开的 双链接列表可能只是为了你试图实现的目标而过分。

堆栈和队列是处理入口数据的相反方法,特别是当它们需要在特定顺序下删除时。 它们通常被视为少于数据结构,而更像是抽象数据类型,这意味着它们代表了一个特定的用途而不是精确的结构。

前提条件

对链接列表的基本理解对于了解列表实现是必不可少的,您可以在 这里获得概述。

虽然不需要理解堆栈和队列,但你应该已经掌握了Big O Notation的基本知识,我写了一篇关于这里的简短介绍(https://andsky.com/tech/tutorials/js-big-o-notation)。

斯塔克斯

堆栈被认为是一个LIFO结构,意思是最后在第一出。我们将项目添加到我们的堆栈中,如果满足了其他条件,例如一个计时器耗尽或完成了一项任务,最近添加的项目是第一个被删除的,也是我们可以互动的唯一一个。

您已经使用了大量的堆栈,如回归呼叫堆栈或标准的JavaScript呼叫堆栈,当您提出非同步请求时。

这个最基本的版本只是一个简单的数组。具有数组实现的堆栈只有O(1),就像一个链接列表一样,因为我们只是操纵尾巴,没有什么需要重新索引。

 1const stack = [];
 2
 3const add = val => stack.push(val);
 4const remove = () => stack.pop();
 5
 6add('one');
 7add('two');
 8add('three');
 9remove();
10console.log(stack); // ["one", "two"]

链接列表有点复杂,我们有我们的正常节点只有一个指针和一些添加删除方法. 如果列表中没有任何东西将其设置为头和尾巴或零,否则更改前面的项目上的指针。

如果您已连接到包含大量节点的数据库,这种方法会更受欢迎. 由于数组被加载到固定大小,链接列表最好只加载必要的数据片段。

 1class Node {
 2  constructor(val) {
 3    this.val = val;
 4    this.next = null;
 5  }
 6};
 7
 8class Stack {
 9  constructor() {
10    this.head = null;
11    this.tail = null;
12    this.length = 0;
13  }
14  add(val) {
15    const newNode = new Node(val);
16
17    if (!this.head) {
18      this.head = newNode;
19      this.tail = newNode;
20    } else {
21      const temp = this.head;
22      this.head = newNode;
23      this.head.next = temp;
24    };
25
26    this.length++;
27    return this;
28  }
29  remove() {
30    if (!this.head) return null;
31
32    let temp = this.head;
33    this.head = this.head.next;
34
35    this.length--;
36    return temp.val;
37  }
38};
39
40let stack = new Stack()
41stack.add('one')
42stack.add('two')
43stack.add('three')
44stack.remove()
45console.log(stack) // two -> one

尾巴

排队是具有FIFO结构的堆栈的反面,意思是第一个排队,这就像站在排队一样,你首先出现,所以你先走。

同样,我们仍然可以做一个数组实现,但这一次它是不同的. 由于我们在删除某些东西时从头开始工作,每个删除意味着我们的计算机需要循环通过剩余的数组并重新索引一切,给我们O(n)

 1const queue = [];
 2
 3const add = val => queue.push(val);
 4const remove = () => queue.shift();
 5
 6add('one');
 7add('two');
 8add('three');
 9remove();
10console.log(queue); // ["two", "three"]

在这种情况下,链接列表几乎总是优于处理较大的数据量,因为它避免了重新索引问题。

无论我们从另一端删除,只要我们添加到哪个端,在这种情况下,我们将添加到尾巴并删除头部。

 1class Queue {
 2  constructor() {
 3    this.head = null;
 4    this.tail = null;
 5    this.length = 0;
 6  }
 7  enqueue(val) {
 8    const newNode = new Node(val);
 9
10    if (!this.head) {
11      this.head = newNode;
12      this.tail = newNode;
13    } else {
14      this.tail.next = newNode;
15      this.tail = newNode;
16    };
17
18    this.length++;
19    return this;
20  }
21  dequeue() {
22    if (!this.head) return null;
23    if (this.head === this.tail) this.last = null;
24    let temp = this.head;
25    this.head = this.head.next;
26
27    this.length--;
28    return temp.val;
29  }
30}
31
32let queue = new Queue();
33queue.enqueue('one');
34queue.enqueue('two');
35queue.enqueue('three');
36queue.dequeue();
37console.log(queue); // two -> three

关闭思想

这可能看起来像是从我们正常的链接列表和阵列中分裂的头发,但随着我们向越来越复杂的结构发展,堆栈和队列将成为我们如何结构和跨越数据的重要组成部分。

Published At
Categories with 技术
Tagged with
comments powered by Disqus