- 双向链表
- 实现双向链表
- 定义节点类
- 定义双向链表类
- append 向双向链表尾部添加一个新元素
- insert 向双向链表的任意位置插入新元素
- getElementAt() 返回双向链表中特定位置的元素
- remove 从双向链表中移除一个元素
- removeAt 从双向链表的特定位置移除一个元素
- removeHead 移除首节点
- removeTail 移除尾结点
- indexOf 返回指定元素在双向链表中的索引
- head 返回首节点
- tail 返回尾结点
- size 返回双向链表包含的元素个数
- isEmpty 判断双向链表是否为空
- clear 清空双向链表
- toString() 返回表示整个双向链表的字符串
- inverseToString() 返回双向链表从尾到头的字符串
- 完整代码
- 双向链表的应用
双向链表
双向链表是链表的一种,它的每个节点中都有两个指针,分别指向上一个节点和下一个节点,所以,从双向链表中的任意一个节点开始,都可以很方便的访问他的前驱节点和后继节点。如下图为双向链表的结构图:
双向链表与普通链表的区别在于,在普通链表中,一个节点只有一个指向下一个节点的指针域,而在双向链表中,有两个指针域,一个指向下一个节点,另一个指向上一个节点,如上图所示。
实现双向链表
定义节点类
双向链表中的节点只是比普通链表中的节点多了一个 prev 指针,我们的双向链表中的节点类可以继承自普通链表的节点类:
class DoublyNode {
constructor(element) {
this.element = element;
this.prev = null;
this.next = null;
}
}
定义双向链表类
DoublyLinkList 类是一种特殊的 LInkList 类,我们只需要扩展
import { defaultEquals } from './utils';
class DoublyLinkList {
constructor(equalsFn = defaultEquals) {
// 链表内部的函数,用于比较链表中的元素是否相等
this.equalsFn = equalsFn;
// 存储双向链表中的元素个数
this.count = 0;
// 保存头节点(注意:head 永远指向头节点)
this.head = null;
// 保存尾结点(注意:tail 永远指向尾结点)
this.tail = null;
}
}
接下来,我们为双向链表定义一些方法:
append(element) 向双向链表尾部添加一个新元素
insert(element, index) 向双向链表的任意位置插入新元素
getElementAt(index) 返回双向链表中特定位置的元素,若元素不存在,返回 undefined
remove(element) 从双向链表中移除一个元素
removeAt(index) 从双向链表的特定位置移除一个元素
removeHead() 移除首节点
removeTail() 移除尾结点
indexOf(element) 返回指定元素在双向链表中的索引,若没有该元素则返回 -1
head() 返回首节点
tail() 返回尾结点
size() 返回双向链表包含的元素个数,即返回链表的长度
isEmpty() 判断双向链表是否为空
clear() 清空双向链表
toString() 返回表示整个双向链表的字符串
inverseToString() 返回双向链表从头到尾的字符串
append 向双向链表尾部添加一个新元素
向双向链表尾部添加一个新元素时,和向普通链表尾部添加新元素一样,也可能有两中情景:
- 双向链表为空,添加的是第一个元素
双向链表不为空,向其追加元素
append(element) {
const node = new DoublyNode();
// 双向链表为空,添加的是第一个元素
if (this.head == null) {
// 让 head 指向新创建的节点
this.head = node;
// 让 tail 属性指向新创建的节点 tail 属性永远指向双向链表的最后一个元素
this.tail = node;
} else {
// 双向链表不为空,向双向链表尾部添加元素
// 将尾部元素的 next 指针指向新节点,新节点就变成了新的尾结点
this.tail.next = node;
// 将新创建的节点的 prev 指针指向旧的尾结点
node.prev = this.tail;
// tail 属性永远是链表最后一个元素的引用
this.tail = node;
}
this.count++;
}
我们来分析一下这两种情景:
第一种情景:向空的双向链表添加一个元素。当我们创建一个双向链表实例时,head 默认会指向 null,也就意味着此时的双向链表是一个空链表,我们是在向双向链表添加第一个元素,因此我们要做的就是让 head 指向这个新建的节点 node,同时也要把 tail 指向 node,即此时的 node 节点既是首节点也是尾结点。
// 双向链表为空,添加的是第一个元素
if (this.head == null) {
// 让 head 指向新创建的节点
this.head = node;
// 让 tail 属性指向新创建的节点 tail 属性永远指向双向链表的最后一个元素
this.tail = node;
}
第二种情景:向一个不为空的双向链表添加新元素。要向双向链表的尾部添加一个新元素,首先需要找到最后一个元素。在双向链表中,tail 属性永远都是对双向链表最后一个元素的引用,因此我们可以很方便的找到双向链表中的最后一个元素。然后将 tail 的 next 指针指向要添加到双向链表的新节点,再将 tail 属性指向新节点,因为 tail 属性永远都是指向双向链表中的最后一个元素。
else {
// 双向链表不为空,向双向链表尾部添加元素
// 将尾部元素的 next 指针指向新节点,新节点就变成了新的尾结点
this.tail.next = node;
// 将新创建的节点的 prev 指针指向旧的尾结点
node.prev = this.tail;
// tail 属性永远是链表最后一个元素的引用
this.tail = node;
}
insert 向双向链表的任意位置插入新元素
insert 方法向双向链表的任意位置插入一个新元素。向双向链表中插入新元素时,需要考虑四种情景:
- 插入的位置不合法,元素插入失败
- 在双向链表的起点位置插入新元素
- 在双向链表的尾部插入新元素
在双向链表的中间插入新元素 ```javascript insert(element, index) { // 插入的位置合法,向双向链表中插入新元素 if (index >=0 && index <= this.count) {
const node = new DoublyNode(element); let current = this.head;
if (index === 0) {
// 在双向链表的头部插入元素
if (this.head == null) { // 双向链表为空链表
// 此时添加的 node 节点既是头节点也是尾节点
this.head = node;
this.tail = node;
} else { // 双向链表不为空,此时添加的 node 将会成为 头节点
// 将 node 节点的 next 指针指向当前的头节点
node.next = this.head;
// 将当前的头节点的 prev 指针指向 node 节点
this.head.prev = node;
// 将 head 指向 node,node成为新的头节点
this.head = node;
}
} else if (index === this.count) {
// 在双向链表的尾部插入元素,此时添加的 node 将会成为 尾节点
current = this.tail;
// 将当前的尾节点的 next 指针指向 node 节点
current.next = node;
// 将 node 节点的 prev 指针指向当前的尾节点
node.prev = current;
// 将 tail 属性指向 node,node成为新的尾节点
this.tail = node;
} else {
// 在双向链表的其他位置插入元素
// 首先获取插入位置的前一个节点
const previous = this.getElementAt(index - 1);
// 插入位置的后一个节点
current = previous.next;
// 将 node 节点插入 previous 和 current 之间
// 则将 node 的 next 指针指向 current,prev 指针指向 previous
node.next = current;
node.prev = previous;
// node 节点插入后,previous 的 next 指针指向 node 节点
previous.next = node;
// current(插入位置的后一个节点) 的 prev 指针指向新插入的 node 节点
current.prev = node;
}
// 新节点插入后,双向链表的长度增加 1 this.count++; // 返回 true,表示元素成功插入到双向链表中 return true; }
// 插入的位置不合法,返回false,表示元素没有添加到双向链表中 return false;
}
下面我们逐一分析这四种情景:
第一种情景:插入的位置不合法。由于我们是根据位置(索引)来插入元素,因此在插入前需要先检查越界值,如果越界了,就返回 false,表示元素没有插入到双向链表中。
```javascript
if (index >=0 && index <= this.count) {
}
// 插入的位置不合法,返回false,表示元素没有添加到双向链表中
return false;
第二种情景:在双向链表的第一个位置(起点) 插入一个新元素。如果双向链表为空,我们只需要把 head 和 tail 属性都指向这个新节点即可,此时的 node 节点既是头节点也是尾结点。如果双向链表不为空,我们就将新加节点 node 的 next 指针指向双向链表中当前的头节点 head,然后将 head 的 prev 指针指向新加节点 node,最后将 head 属性指向 node 节点,新插入的 node 节点就成为了新的头节点。
if (index === 0) {
// 在双向链表的头部插入元素
if (this.head == null) { // 双向链表为空链表
// 此时添加的 node 节点既是头节点也是尾节点
this.head = node;
this.tail = node;
} else { // 双向链表不为空,此时添加的 node 将会成为 头节点
// 将 node 节点的 next 指针指向当前的头节点
node.next = this.head;
// 将当前的头节点的 prev 指针指向 node 节点
this.head.prev = node;
// 将 head 指向 node,node成为新的头节点
this.head = node;
}
}
第三种情景:在双向链表的尾部添加一个新元素。在这种情景下,我们需要把最后一个节点的 next 指针指向 node 节点,然后把 node 节点的 prev 指针指向双向链表中当前的尾结点,最后将 tail 属性指向 node 节点,这样新添加的node节点就成为了新的尾结点了。
else if (index === this.count) {
// 在双向链表的尾部插入元素,此时添加的 node 将会成为 尾节点
current = this.tail;
// 将当前的尾节点的 next 指针指向 node 节点
current.next = node;
// 将 node 节点的 prev 指针指向当前的尾节点
node.prev = current;
// 将 tail 属性指向 node,node成为新的尾节点
this.tail = node;
}
第四种情景:在链表的中间插入一个新元素。在这种情景下,我们需要迭代双向链表找到要插入的位置。调用 getElementAt 方法找到插入位置的前一个节点 previous,然后将 插入位置的下一个节点 previous.next 赋值给 current 变量。我们现在要做的就是在 previous 和 current 之间插入新节点,首先将新插入的节点 node 的next 指针指向 current ,previous 的 next 指针指向 node 节点,然后再将 current 的 prev 指针指向 node 节点,node 节点的 prev 指针指向 previous ,这样,新节点 node 就插入了 previous 和 current 之间。
else {
// 在双向链表的其他位置插入元素
// 首先获取插入位置的前一个节点
const previous = this.getElementAt(index - 1);
// 插入位置的后一个节点
current = previous.next;
// 将 node 节点插入 previous 和 current 之间
// 则将 node 的 next 指针指向 current,prev 指针指向 previous
node.next = current;
node.prev = previous;
// node 节点插入后,previous 的 next 指针指向 node 节点
previous.next = node;
// current(插入位置的后一个节点) 的 prev 指针指向新插入的 node 节点
current.prev = node;
}
getElementAt() 返回双向链表中特定位置的元素
getElementAt 方法返回双向链表中特定位置的元素,若元素不存在,返回 undefined
getElementAt(index) {
// 传入的位置合法,迭代双向链表查找目标位置的元素
if (index >= 0 && index <= this.count) {
// 从第一个元素迭代双向链表
let node = this.head;
for (let i = 0; i < index && node != null; i++) {
// 迭代整个双向链表直到目标 index,结束循环时,此时的 node 元素是目标index 的前一个元素
// 因此需要使用 node.next 来获取目标 index 的元素
node = node.next;
}
return node;
}
// 传入的位置不合法,即在双向链表中找不到该位置的节点,返回 undefined
return undefined;
}
代码分析:
我们首先会对传入的 index 参数做合法性检查,如果传入的位置是不合法的参数,我们会返回 undefined,因为这个位置在双向链表中不存在。
然后从双向链表的第一个节点开始,迭代整个链表直到目标index,当结束循环时,node 就是我们要查找的目标节点。
remove 从双向链表中移除一个元素
remove(element) {
const index = this.indexOf(element);
return this.removeAt(index)
}
removeAt 从双向链表的特定位置移除一个元素
removeAt 方法从双向链表的特定位置移除一个元素。我们需要处理四种情景:
- 移除的位置不合法,元素移除失败
- 移除双向链表中的第一个元素
- 移除双向链表的最后一个元素
- 移除双向链表中间的元素
removeAt(index) {
if (index >= 0 && index < this.count) {
// current 变量是对双向链表中第一个节点的引用
let current = this.head;
if (index === 0) {
// 删除双向链表的头部元素
// 删除头部元素,把 head 指向第二个节点即可
this.head = this.head.next;
if (this.count === 1) {
// 链表只有一个节点时,该节点的 prev 指针和 next 指针都为 null
// 如果链表中只有一个节点,删除了头部节点,就变成了空的双向链表
// 因此需要将 tail 设为 null
this.tail = null;
} else {
// 将新的头部节点的 prev 指针设为 null
this.head.prev = null;
}
} else if (index === this.count - 1) {
// 删除双向链表的尾部元素
current = this.tail;
// tail 是对最后一个元素的引用,删除最后一个元素,直接将 tail 的引用更新为倒数第二个元素即可
this.tail = current.prev;
// 将新的尾部元素的 next 指针设为 null
this.tail.next = null;
} else {
// 删除双向链表的中间元素
// current 变量就是要删除的元素
current = this.getElementAt(index);
// 要删除元素的前一个元素
const previous = current.prev;
// 将 previous 的 next 指针指向要删除元素的下一个元素
previous.next = current.next;
// 将 要删除元素的下一个元素的 prev 指针指向 previous
current.next.prev = previous;
}
this.count--;
return current.element;
}
return undefined;
}
接下来,我们分析一下这四种情景:
第一种情景:移除的位置不合法。由于 removeAt 方法是根据位置 (index) 移除双向链表中的元素,因此我们首先需要对 index 进行有效性检查。从 0(包括 0 )到双向链表的长度(count - 1)都是有效的位置。如果 index 不是有效的位置,就返回 undefined,即没有从链表中移除元素。
第二种情景:移除双向链表中的第一个元素。要移除双向链表中的第一个元素,我们把 head 的引用改为双向链表中的第二个元素(current.next)即可,同时还要将链表中的第二个元素的 prev 指针设为null,因为双向链表的第一个元素的 prev 指针永远都为 null(或 undefined)。如果双向链表中只有一个元素,移除了双向链表的第一个元素(也是最后一个元素),那么双向链表就为空了,此时我们还需要将 tail 设为 null。
if (index === 0) {
// 删除双向链表的头部元素
// 删除头部元素,把 head 指向第二个节点即可
this.head = this.head.next;
if (this.count === 1) {
// 链表只有一个节点时,该节点的 prev 指针和 next 指针都为 null
// 如果链表中只有一个节点,删除了头部节点,就变成了空的双向链表
// 因此需要将 tail 设为 null
this.tail = null;
} else {
// 将新的头部节点的 prev 指针设为 null
this.head.prev = null;
}
}
第三种情景:移除双向链表的最后一个元素。因为我们已经有对双向链表最后一个元素的引用,因此我们可以很方便地移除最后一个元素。我们只需将 tail 的引用指向双向链表中倒数第二个元素即可,既然 tail 指向了倒数第二个元素,我们只需要将 next 指针设为 null 就可以了。这样,双向链表中的最后一个元素自然就被移除了。
else if (index === this.count - 1) {
// 删除双向链表的尾部元素
current = this.tail;
// tail 是对最后一个元素的引用,删除最后一个元素,直接将 tail 的引用更新为倒数第二个元素即可
this.tail = current.prev;
// 将新的尾部元素的 next 指针设为 null
this.tail.next = null;
}
第四种情景:从双向链表中间移除一个元素。首先需要迭代双向链表,直到找到要移除的位置。我们将要移除的元素赋值给 current 变量,那么要移除元素的上一个元素就是 previous = current.prev,要移除元素的下一个元素就是
current.next,我们只需要将 previous 的 next 指针指向 current.next,current.next 的 prev 指针指向 previous ,就可以断开与要移除元素的连接,从而将要移除元素移除掉。
else {
// 删除双向链表的中间元素
// current 变量就是要删除的元素
current = this.getElementAt(index);
// 要删除元素的前一个元素
const previous = current.prev;
// 将 previous 的 next 指针指向要删除元素的下一个元素
previous.next = current.next;
// 将 要删除元素的下一个元素的 prev 指针指向 previous
current.next.prev = previous;
}
removeHead 移除首节点
removeHead() {
if (this.head == null) {
return undefined;
}
const current = this.head;
// 删除头部元素,把 head 指向第二个节点即可
this.head = this.head.next;
if (this.count === 1) {
// 链表只有一个节点时,该节点的 prev 指针和 next 指针都为 null
// 如果链表中只有一个节点,删除了头部节点,就变成了空的双向链表
// 因此需要将 tail 设为 null
this.tail = null;
} else {
// 将新的头部节点的 prev 指针设为 null
this.head.prev = null;
}
return current.element;
}
如果双向链表只有一个节点,移除首节点,双向链表就为空了,此时除了要将 head 设为 null,还需要将 tail 也设为 null;
如果双向链表中不止一个节点,移除首节点,只需将 head 指向双向链表的第二个节点,然后将第二个节点的 prev 指针设为 null 就可以了。
removeTail 移除尾结点
removeTail() {
// 空链表
if (this.head == null) {
return undefined;
}
// current 变量是对尾结点的引用
const current = this.tail;
// tail 是对最后一个元素的引用,删除最后一个元素,直接将 tail 的引用更新为倒数第二个元素即可
this.tail = current.prev;
// 将新的尾部元素的 next 指针设为 null
this.tail.next = null;
return current.element;
}
因为 tail 属性是对双向链表的最后一个节点的引用,要移除尾节点,直接将 tail 的引用更新为双向链表的倒数第二个节点,并将倒数第二个节点的 next 指针设为 null 即可。
indexOf 返回指定元素在双向链表中的索引
indexOf 方法接收一个元素的值,如果在双向链表中找到了它,就返回该元素的位置,否则就返回 -1,表示在双向链表中没有找到该元素
indexOf(element) {
// current 变量是对双向链表中第一个元素(即首节点)的引用
let current = this.head;
// 迭代链表,从 head(索引为 0)开始
let index = 0;
while(current != null) {
// 比较当前节点的元素与目标元素是否相等,若相等,当前节点就是我们要找的节点,返回当前节点的位置
if (this.equalsFn(element, current.element)) {
return index;
}
// 不是我们要找的节点,继续迭代下一个节点
index++;
current = current.next;
}
// 双向链表为空或者迭代到双向链表尾部,没有找到目标元素,返回 -1
return -1
}
代码分析:
从双向链表中查找某个节点的位置,需要对双向链表进行迭代。我们从第一个元素 head (索引 0) 开始,一直到 current 为 null为止,也就是迭代到双向链表的尾部。
在每次迭代时,我们都会验证 current 节点的元素和目标元素是否相等,如果相等,当前位置的元素就是我们要找的元素,返回该元素的位置。如果不是我们要找的元素,则继续迭代下一个节点。
如果双向链表为空或者迭代到了双向链表的尾部,都没有找到我们想要的元素,就返回 -1。
head 返回首节点
head() {
return this.head;
}
tail 返回尾结点
tail() {
return this.tail;
}
size 返回双向链表包含的元素个数
size 方法返回双向链表的节点个数,即返回双向链表的长度。DoublyLinkList 是我们自己构建的类,其长度是我们使用 count 变量来控制的,因此返回双向链表的长度,就是返回 count 变量。
size() {
return this.count;
}
isEmpty 判断双向链表是否为空
isEmpty 方法判断双向链表是否为空。如果双向链表中没有元素,isEmpty 就会返回 true,否则返回 false
isEmpty() {
return this.size() === 0
}
clear 清空双向链表
clear() {
this.head = null;
this.tail = null;
this.count = 0;
}
toString() 返回表示整个双向链表的字符串
toString 方法将 DoublyLinkList 对象转换成一个字符串,然后返回双向链表内容的字符串。
toString() {
if (this.head == null) {
return '';
}
let objString = `${this.head.element}`;
let current = this.head.next;
while(current != null) {
objString = `${objString}, ${current.element}`;
current = current.next;
}
return objString;
}
代码分析:
首先,如果双向链表为空,我们就返回一个空字符串;
如果双向链表不为空,我们首先用双向链表第一个元素的值来初始化方法返回的字符串objString。然后迭双向代链表的所有其它元素,将元素值添加到字符串 objString 上;
当迭代完双向链表后,返回双向链表内容的字符串。
inverseToString() 返回双向链表从尾到头的字符串
inverseToString 方法将 DoublyLinkList 对象转换成一个字符串,然后返回双向链表从尾到头的字符串。
inverseToString() {
if (this.tail != null) {
return ''
}
let objString = `${this.tail.element}`;
let previous = this.tail.prev;
while(previous != null) {
objString = `${objString}, ${previous.element}`;
previous = previous.prev;
}
return
}
代码分析:
首先,如果双向链表为空,我们就返回一个空字符串;
如果双向链表不为空,我们首先用双向链表的尾部元素的值来初始化方法返回的字符串objString。然后从尾部开始往前迭双向代链表的所有其它元素,将元素值添加到字符串 objString 上;
当迭代完双向链表后,返回双向链表内容的字符串。
完整代码
function defaultEquals(a, b) {
return a === b;
}
class DoublyNode {
constructor(element) {
this.element = element;
this.prev = null;
this.next = null;
}
}
class DoublyLinkList {
constructor(equalsFn = defaultEquals) {
this.equalsFn = equalsFn;
this.count = 0;
this.head = null;
this.tail = null;
}
append(element) {
const node = new DoublyNode(element);
if (this.head == null) {
this.head = node;
this.tail = node;
} else {
this.tail.next = node;
node.prev = this.tail;
this.tail = node;
}
this.count++;
}
insert(element, index) {
if (index >=0 && index <= this.count) {
const node = new DoublyNode(element);
let current = this.head;
if (index === 0) {
if (this.head == null) {
this.head = node;
this.tail = node;
} else {
node.next = this.head;
this.head.prev = node;
this.head = node;
}
} else if (index === this.count) {
current = this.tail;
current.next = node;
node.prev = current;
this.tail = node
} else {
const previous = this.getElementAt(index);
current = previous.next;
node.next = current;
node.prev = previous;
current.prev = node;
previous.next = node;
}
this.count++;
return true;
}
return false;
}
getElementAt(index) {
if (index >= 0 && index <= this.count) {
let node = this.head;
for (let i = 0; i < index && node != null; i++) {
node = node.next;
}
return node;
}
return undefined;
}
remove(element) {
const index = this.indexOf(element);
return this.removeAt(index);
}
removeAt(index) {
if (index >= 0 && index < this.count) {
let current = this.head;
if (index === 0) {
this.head = this.head.next;
if (this.count === 1) {
this.tail = null;
} else {
this.head.prev = null;
}
} else if (index === this.count - 1) {
current = this.tail;
this.tail = current.prev;
this.tail.next = null;
} else {
current = this.getElementAt(index);
const previous = current.prev;
previous.next = current.next;
current.next.prev = previous;
}
this.count--;
return current.element;
}
return undefined;
}
removeHead() {
if (this.head == null) {
return undefined;
}
const current = this.head;
this.head = this.head.next;
if (this.count === 1) {
this.tail = null;
} else {
this.head.prev = null;
}
return current.element;
}
removeTail() {
if (this.head = null) {
return undefined;
}
const current = this.tail;
this.tail = current.prev;
this.tail.next = null;
return current.element;
}
indexOf(element) {
let current = this.head;
let index = 0;
while(current != null) {
if (this.equalsFn(element, current.element)) {
return index;
}
index++;
current = current.next;
}
return -1;
}
head() {
return this.head;
}
tail() {
return this.tail;
}
size() {
return this.count;
}
isEmpty() {
return this.size() === 0;
}
clear() {
this.head = null;
this.tail = null;
this.count = 0;
}
toString() {
if (this.head == null) {
return '';
}
let objString = `${this.head.element}`;
let current = this.head.next;
while(current != null) {
objString = `${objString}, ${current.element}`;
current = current.next;
}
return objString;
}
inverseToString() {
if (this.head == null) {
return '';
}
let objString = `${this.tail.element}`;
let previous = this.tail.prev;
while(previous != null) {
objString = `${objString}, ${previous.element}`;
previous = previous.prev;
}
return objString;
}
}
双向链表的应用
基于双向链表实现栈
使用双向链表来实现栈,而不是链表,是因为相对于栈来说,我们会向链表尾部添加元素,也会从链表尾部移除元素,我们实现的双向链表有最后一个元素的引用,无需迭代就能获取它。双向链表可以直接获取尾部的元素,减少过程消耗,它的时间复杂度和原始的 Stack 实现相同,为 O(1)。
class Stack {
constructor() {
this.items = new DoublyList();
}
// 添加元素到栈
push(element) {
this.items.append(element);
}
// 从栈顶移除元素
pop() {
if (this.isEmpty()) {
return undefined;
}
return this.items.removeAt(this.size() - 1);
}
// 查看栈顶元素
peek() {
if (this.isEmpty()) {
return undefined'
}
return this.items.tail();
}
// 检查栈是否为空
isEmtpy() {
return this.items.isEmpty();
}
// 清空栈
clear() {
this.items.clear()
}
// 返回栈里的元素个数
size() {
return this.items.size()
}
// 打印栈
print() {
return this.items.inverseToString()
}
}
基于双向链表实现队列
class Queue {
constructor() {
this.items = new DoublyList();
}
// 入队列
enqueue(element) {
this.items.append(element);
}
// 出队列
dequeue() {
if (this.isEmpty()) {
return undefined;
}
return this.items.removeHead();
}
// 返回队首
head() {
if (this.isEmpty()) {
return undefined;
}
return this.items.head();
}
// 返回队尾
tail() {
if(this.isEmpty()) {
return undefined;
}
return this.items.tail();
}
// 判断队列是否为空
isEmpty() {
return this.items.isEmpty();
}
// 清空队列
clear() {
this.items.clear();
}
// 返回队列长度
size() {
this.items.size()
}
// 打印队列
print() {
this.items.toString()
}
}
基于双向链表实现双端队列
class Deque {
constructor() {
this.items = new DoublyList()
}
// 在双端队列的前端添加元素
addFront(element) {
this.items.append(element);
}
// 在双端队列的后端添加元素
addBack(element) {
this.items.insert(element, this.size() - 1);
}
// 从双端队列的前端移除第一个元素
removeFront() {
if (this.isEmpty()) {
return undefined;
}
return this.items.removeHead();
}
// 从双端队列的后端移除一个元素
removeBack() {
if (this.isEmpty()) {
return undefined;
}
return this.items.removeTail();
}
// 查看双端队列的前端的第一个元素
peekFront() {
if (this.isEmpty()) {
return undefined;
}
return this.items.head();
}
// 查看双端队列的后端的第一个元素
peekBack() {
if (this.isEmpty()) {
return this.items.tail()
}
}
// 检查双端队列是否为空
isEmpty() {
return this.items.isEmpty();
}
// 清空双端队列
clear() {
return this.items.clear();
}
// 返回双端队列的长度
size() {
return this.items.size();
}
// 输出双端队列从队列前端到队列后端的字符串
toString() {
return this.items.toString();
}
}