双向链表
双向链表的查找 和 插入首元素尾元素 和删除中间元素和插入中间元素的动图效果
简单来说就是链表上多了一个向后连接的last
双端队列
后面介绍了
数组
1. 初始化数组
可以使用 [] 创建一个空数组,也可以在创建时填充初始值:
// 空数组
let arr1 = [];
// 带初始值的数组
let arr2 = [1, 2, 3, 4];
当然也可以var arr = new Array(5).fill(0) 直接创建填充满0的数组
2. 访问元素
数组的元素通过索引访问,索引从 0 开始:
let arr = [10, 20, 30];
console.log(arr[0]); // 输出: 10
console.log(arr[1]); // 输出: 20
我们发现数组首个元素的索引为 0 ,这似乎有些反直觉,因为从 1 开始计数会更自然。但从地址计算公式的角度看,索引本质上是内存地址的偏移量。首个元素的地址偏移量是 0 ,因此它的索引为 0 是合理的。
3. 插入元素
可以使用 push() 方法在数组末尾插入元素,或者使用 unshift() 方法在数组开头插入元素:
let arr = [1, 2, 3];
// 在末尾插入
arr.push(4); // [1, 2, 3, 4]
// 在开头插入
arr.unshift(0); // [0, 1, 2, 3, 4]
push(),unshift()两种方法
push()我比较熟悉,unshift()不太熟悉。就是在开头插入,因为JS中数组是动态的,自动添加了一位,在Java中数组就不能这样搞,只有ArrayList能这样玩。
当然我们还可以用splice插入元素。
假设我们有一个数组 [1, 2, 4, 5],我们想在索引 2 的位置插入元素 3,可以这样做:
let arr = [1, 2, 4, 5];
// 使用 splice() 插入元素
arr.splice(2, 0, 3); // 第一个参数是插入位置,第二个参数是删除元素个数,第三个参数是要插入的元素
console.log(arr); // 输出: [1, 2, 3, 4, 5]
这个是我没想到的,感觉是JS独有的方法。
4. 删除元素
可以使用 pop() 方法删除数组末尾的元素,使用 shift() 方法删除数组开头的元素,或者使用 splice() 方法删除指定位置的元素:
let arr = [1, 2, 3, 4];
// 删除末尾元素
arr.pop(); // [1, 2, 3]
// 删除开头元素
arr.shift(); // [2, 3]
// 删除指定位置的元素
arr.splice(1, 1); // 从索引1开始删除1个元素,结果: [2]
如上插入元素
5. 遍历数组
可以使用 for 循环、forEach() 方法或其他数组遍历方法:
let arr = [1, 2, 3, 4];
// 使用 for 循环
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// 使用 forEach()
arr.forEach(element => console.log(element));
这里可以复习一个知识点。for of ; for in 和 forEach 和 for的区别
1. for...of
for...of 用于遍历可迭代对象(如数组、字符串、Set、Map等)的元素。它直接访问元素值。
let arr = [1, 2, 3];
for (const value of arr) {
console.log(value); // 输出: 1, 2, 3
}
- 适用场景:适用于遍历数组、字符串等可迭代对象的元素。
- 优点:简洁明了,适合直接操作元素值。
2. for...in
for...in 用于遍历对象的属性(包括继承的属性)。对于数组,它会遍历数组的索引。
let obj = { a: 1, b: 2, c: 3 };
for (const key in obj) {
console.log(key, obj[key]); // 输出: a 1, b 2, c 3
}
let arr = [10, 20, 30];
for (const index in arr) {
console.log(index, arr[index]); // 输出: 0 10, 1 20, 2 30
}
- 适用场景:适用于遍历对象的属性(不推荐用于数组)。
- 优点:可以遍历对象的所有属性,包括继承的属性。
3. forEach()
forEach() 是数组的方法,用于遍历数组的元素。它接收一个回调函数,该函数会在每个元素上调用。
let arr = [1, 2, 3];
arr.forEach((value, index) => {
console.log(index, value); // 输出: 0 1, 1 2, 2 3
});
- 适用场景:适用于数组,且当你需要在遍历时执行特定操作时。
- 优点:语法简洁,支持函数式编程风格。不会中途退出遍历。
4. for
for 循环是最基本的循环结构,可以用于遍历数组、对象、数字等。
let arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]); // 输出: 1, 2, 3
}
- 适用场景:适用于任何需要循环的情况,包括数组、对象、数字等。
- 优点:灵活强大,可以用来遍历各种结构,适合需要控制循环流程的情况。
当然除了这些以外还有比较特殊用法的
1. map()
map() 方法创建一个新数组,其结果是通过调用提供的函数处理数组的每个元素得来的。
let arr = [1, 2, 3];
let newArr = arr.map(value => value * 2);
console.log(newArr); // 输出: [2, 4, 6]
- 适用场景:当你需要对数组的每个元素进行操作并生成新数组时。
2. filter()
filter() 方法创建一个新数组,其中包含所有通过测试的元素。
let arr = [1, 2, 3, 4];
let filteredArr = arr.filter(value => value > 2);
console.log(filteredArr); // 输出: [3, 4]
- 适用场景:当你需要从数组中筛选出符合特定条件的元素时。
3. reduce()
reduce() 方法对数组中的每个元素执行指定的函数,并将结果汇总为单个值。
let arr = [1, 2, 3, 4];
let sum = arr.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 输出: 10
- 适用场景:当你需要对数组中的元素进行累积计算时,比如求和、求最大值等。
4. some()
some() 方法测试数组中的某些元素是否通过了指定的测试函数。
let arr = [1, 2, 3, 4];
let hasEven = arr.some(value => value % 2 === 0);
console.log(hasEven); // 输出: true
- 适用场景:当你需要检查数组中是否有元素满足某个条件时。
5. every()
every() 方法测试数组中的所有元素是否都通过了指定的测试函数。
let arr = [2, 4, 6, 8];
let allEven = arr.every(value => value % 2 === 0);
console.log(allEven); // 输出: true
- 适用场景:当你需要检查数组中的所有元素是否都满足某个条件时。
6. find()
find() 方法返回数组中满足测试函数的第一个元素,如果没有找到则返回 undefined。
let arr = [5, 12, 8, 130, 44];
let found = arr.find(value => value > 10);
console.log(found); // 输出: 12
- 适用场景:当你需要找到数组中第一个满足条件的元素时。
7. findIndex()
findIndex() 方法返回数组中满足测试函数的第一个元素的索引,如果没有找到则返回 -1。
let arr = [5, 12, 8, 130, 44];
let index = arr.findIndex(value => value > 10);
console.log(index); // 输出: 1
- 适用场景:当你需要找到数组中第一个满足条件的元素的索引时。
8. forEachRight()
forEachRight() 是一些库(如 Lodash)提供的反向遍历数组的方法,JavaScript 原生没有。它的作用是从数组的最后一个元素开始向前遍历。
// Lodash 示例
_.forEachRight([1, 2, 3], function(value) {
console.log(value); // 输出: 3, 2, 1
});
- 适用场景:当你需要从数组的末尾开始遍历时(需要借助外部库)。
6. 查找元素
可以使用 indexOf() 查找元素的位置,或者使用 find() 查找满足条件的元素:
let arr = [10, 20, 30];
// 查找位置
let index = arr.indexOf(20); // 1
// 查找元素
let found = arr.find(element => element > 15); // 20
其实除了这些定义好的方法以外,还能自己定义去查找元素。
7. 扩容数组
JavaScript 数组的大小是动态的,不需要手动扩容。当数组达到容量限制时,JavaScript 会自动扩展数组的大小。
let arr = [1, 2, 3];
arr.length = 10; // 扩容数组,但原有元素不会改变
console.log(arr); // [1, 2, 3, <7 empty items>]
8. 排序列表
nums.sort((a, b) => a - b); // 排序后,列表元素从小到大排列
链表
//链表.js
class ListNode {
constructor(value = null, next = null) {
this.value = value;
this.next = next;
}
}
//初始化链表
class LinkedList {
constructor() {
this.head = null;
this.tail = null;
this.size = 0;
}
//构造函数初始化
append(value) {
const newNode = new ListNode(value);
if (this.head === null) {
this.head = newNode;
this.tail = newNode;
} else {
this.tail.next = newNode;
this.tail = newNode;
}
this.size++;
}
//添加节点
prepend(value) {
const newNode = new ListNode(value, this.head);
this.head = newNode;
if (this.size === 0) {
this.tail = newNode;
}
this.size++;
}
insertAt(value, index) {
if (index < 0 || index > this.size) {
throw new Error('Index out of bounds');
}
if (index === 0) {
this.prepend(value);
} else if (index === this.size) {
this.append(value);
} else {
const newNode = new ListNode(value);
let current = this.head;
for (let i = 0; i < index - 1; i++) {
current = current.next;
}
newNode.next = current.next;
current.next = newNode;
this.size++;
}
}
//插入节点
remove(value) {
if (this.size === 0) return;
if (this.head.value === value) {
this.head = this.head.next;
if (this.size === 1) {
this.tail = null;
}
this.size--;
return;
}
let current = this.head;
while (current.next !== null && current.next.value !== value) {
current = current.next;
}
if (current.next !== null) {
current.next = current.next.next;
if (current.next === null) {
this.tail = current;
}
this.size--;
}
}
//移除节点
removeAt(index) {
if (index < 0 || index >= this.size) {
throw new Error('Index out of bounds');
}
if (index === 0) {
this.head = this.head.next;
if (this.size === 1) {
this.tail = null;
}
this.size--;
return;
}
let current = this.head;
for (let i = 0; i < index - 1; i++) {
current = current.next;
}
current.next = current.next.next;
if (current.next === null) {
this.tail = current;
}
this.size--;
}
//移除目标节点
find(value) {
let current = this.head;
while (current !== null) {
if (current.value === value) {
return current;
}
current = current.next;
}
return null;
}
//查找
getAt(index) {
if (index < 0 || index >= this.size) {
throw new Error('Index out of bounds');
}
let current = this.head;
for (let i = 0; i < index; i++) {
current = current.next;
}
return current;
}
//拿到下标的
printList() {
let current = this.head;
let result = [];
while (current !== null) {
result.push(current.value);
current = current.next;
}
return result;
}
//打印链表
}
export { LinkedList };
链表的区分
- 单向链表
- 双向链表
- 环形链表 单向链表通常用于实现栈、队列、哈希表和图等数据结构。
- 栈与队列:当插入和删除操作都在链表的一端进行时,它表现的特性为先进后出,对应栈;当插入操作在链表的一端进行,删除操作在链表的另一端进行,它表现的特性为先进先出,对应队列。
- 哈希表:链式地址是解决哈希冲突的主流方案之一,在该方案中,所有冲突的元素都会被放到一个链表中。
- 图:邻接表是表示图的一种常用方式,其中图的每个顶点都与一个链表相关联,链表中的每个元素都代表与该顶点相连的其他顶点。
双向链表常用于需要快速查找前一个和后一个元素的场景。
- 高级数据结构:比如在红黑树、B 树中,我们需要访问节点的父节点,这可以通过在节点中保存一个指向父节点的引用来实现,类似于双向链表。
- 浏览器历史:在网页浏览器中,当用户点击前进或后退按钮时,浏览器需要知道用户访问过的前一个和后一个网页。双向链表的特性使得这种操作变得简单。
- LRU 算法:在缓存淘汰(LRU)算法中,我们需要快速找到最近最少使用的数据,以及支持快速添加和删除节点。这时候使用双向链表就非常合适。
环形链表常用于需要周期性操作的场景,比如操作系统的资源调度。
- 时间片轮转调度算法:在操作系统中,时间片轮转调度算法是一种常见的 CPU 调度算法,它需要对一组进程进行循环。每个进程被赋予一个时间片,当时间片用完时,CPU 将切换到下一个进程。这种循环操作可以通过环形链表来实现。
- 数据缓冲区:在某些数据缓冲区的实现中,也可能会使用环形链表。比如在音频、视频播放器中,数据流可能会被分成多个缓冲块并放入一个环形链表,以便实现无缝播放。
栈
栈(Stack)是一种数据结构,用于管理和存储数据,其核心操作基于“后进先出”(LIFO, Last In, First Out)的原则。栈的基本操作包括入栈(push)、出栈(pop)和查看栈顶元素(peek)。
栈的基本概念
-
后进先出(LIFO) :在栈中,最新放入的元素最先被移除。可以想象成一堆盘子,最后放上去的盘子必须最先取走。
-
基本操作:
- 入栈(push) :将一个元素添加到栈顶。
- 出栈(pop) :移除并返回栈顶的元素。
- 查看栈顶元素(peek) :返回栈顶的元素但不移除它。
- 检查栈是否为空(isEmpty) :检查栈中是否还有元素。
栈的实现
栈可以使用数组或链表来实现。下面是用 JavaScript 实现栈的两种方式:数组和链表。
使用数组实现栈
在 JavaScript 中,数组已经提供了栈的基本操作:
class Stack {
constructor() {
this.items = []; // 用于存储栈的元素
}
// 入栈操作
push(element) {
this.items.push(element);
}
// 出栈操作
pop() {
if (this.isEmpty()) {
return 'Stack is empty'; // 如果栈为空,返回提示
}
return this.items.pop();
}
// 查看栈顶元素
peek() {
if (this.isEmpty()) {
return 'Stack is empty'; // 如果栈为空,返回提示
}
return this.items[this.items.length - 1];
}
// 检查栈是否为空
isEmpty() {
return this.items.length === 0;
}
// 获取栈的大小
size() {
return this.items.length;
}
// 清空栈
clear() {
this.items = [];
}
}
// 示例
const stack = new Stack();
stack.push(1);
stack.push(2);
console.log(stack.peek()); // 输出: 2
console.log(stack.pop()); // 输出: 2
console.log(stack.peek()); // 输出: 1
console.log(stack.isEmpty()); // 输出: false
console.log(stack.size()); // 输出: 1
使用链表实现栈
使用链表实现栈的好处是能高效地进行入栈和出栈操作。下面是链表实现栈的代码:
class ListNode {
constructor(value = null, next = null) {
this.value = value;
this.next = next;
}
}
class Stack {
constructor() {
this.top = null; // 栈顶元素
this.size = 0; // 栈的大小
}
// 入栈操作
push(value) {
this.top = new ListNode(value, this.top);
this.size++;
}
// 出栈操作
pop() {
if (this.isEmpty()) {
return 'Stack is empty'; // 如果栈为空,返回提示
}
const value = this.top.value;
this.top = this.top.next;
this.size--;
return value;
}
// 查看栈顶元素
peek() {
if (this.isEmpty()) {
return 'Stack is empty'; // 如果栈为空,返回提示
}
return this.top.value;
}
// 检查栈是否为空
isEmpty() {
return this.size === 0;
}
// 获取栈的大小
size() {
return this.size;
}
// 清空栈
clear() {
this.top = null;
this.size = 0;
}
}
// 示例
const stack = new Stack();
stack.push(1);
stack.push(2);
console.log(stack.peek()); // 输出: 2
console.log(stack.pop()); // 输出: 2
console.log(stack.peek()); // 输出: 1
console.log(stack.isEmpty()); // 输出: false
console.log(stack.size()); // 输出: 1
栈的应用场景
栈在计算机科学中有广泛的应用,包括:
- 函数调用管理:函数调用栈用于跟踪函数调用及返回。
- 表达式求值:使用栈来处理数学表达式(如中缀表达式转后缀表达式)。
- 回溯算法:在解决问题时,栈用于记录状态,方便回退(如迷宫问题)。
- 撤销操作:在文本编辑器中,栈用于实现撤销和重做功能。
队列
队列(Queue)是一种数据结构,用于管理和存储数据,其核心操作基于“先进先出”(FIFO, First In, First Out)的原则。队列中的数据项按照它们被插入的顺序排列,最早插入的数据项会被最先移除。
队列的基本概念
-
先进先出(FIFO) :在队列中,最早插入的元素最先被移除。可以想象成排队等候的人,最早到达的人最早被服务。
-
基本操作:
- 入队(enqueue) :将一个元素添加到队列的末尾。
- 出队(dequeue) :移除并返回队列的前端元素。
- 查看队列前端元素(peek/front) :返回队列前端的元素但不移除它。
- 检查队列是否为空(isEmpty) :检查队列中是否还有元素。
队列的实现
队列可以使用数组或链表来实现。下面是用 JavaScript 实现队列的两种方式:数组和链表。
使用数组实现队列
在 JavaScript 中,数组提供了实现队列的基本方法,如 push() 和 shift()。push() 用于入队,shift() 用于出队。
class Queue {
constructor() {
this.items = []; // 用于存储队列的元素
}
// 入队操作
enqueue(element) {
this.items.push(element);
}
// 出队操作
dequeue() {
if (this.isEmpty()) {
return 'Queue is empty'; // 如果队列为空,返回提示
}
return this.items.shift();
}
// 查看队列前端元素
front() {
if (this.isEmpty()) {
return 'Queue is empty'; // 如果队列为空,返回提示
}
return this.items[0];
}
// 检查队列是否为空
isEmpty() {
return this.items.length === 0;
}
// 获取队列的大小
size() {
return this.items.length;
}
// 清空队列
clear() {
this.items = [];
}
}
// 示例
const queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
console.log(queue.front()); // 输出: 1
console.log(queue.dequeue()); // 输出: 1
console.log(queue.front()); // 输出: 2
console.log(queue.isEmpty()); // 输出: false
console.log(queue.size()); // 输出: 1
使用链表实现队列
使用链表实现队列的好处是能高效地进行入队和出队操作。下面是链表实现队列的代码:
class ListNode {
constructor(value = null, next = null) {
this.value = value;
this.next = next;
}
}
class Queue {
constructor() {
this.front = null; // 队列前端元素
this.rear = null; // 队列末尾元素
this.size = 0; // 队列的大小
}
// 入队操作
enqueue(value) {
const newNode = new ListNode(value);
if (this.isEmpty()) {
this.front = newNode;
this.rear = newNode;
} else {
this.rear.next = newNode;
this.rear = newNode;
}
this.size++;
}
// 出队操作
dequeue() {
if (this.isEmpty()) {
return 'Queue is empty'; // 如果队列为空,返回提示
}
const value = this.front.value;
this.front = this.front.next;
if (this.front === null) {
this.rear = null;
}
this.size--;
return value;
}
// 查看队列前端元素
front() {
if (this.isEmpty()) {
return 'Queue is empty'; // 如果队列为空,返回提示
}
return this.front.value;
}
// 检查队列是否为空
isEmpty() {
return this.size === 0;
}
// 获取队列的大小
size() {
return this.size;
}
// 清空队列
clear() {
this.front = null;
this.rear = null;
this.size = 0;
}
}
// 示例
const queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
console.log(queue.front()); // 输出: 1
console.log(queue.dequeue()); // 输出: 1
console.log(queue.front()); // 输出: 2
console.log(queue.isEmpty()); // 输出: false
console.log(queue.size()); // 输出: 1
队列的应用场景
队列在计算机科学中有广泛的应用,包括:
- 任务调度:操作系统中的任务调度程序使用队列来管理进程或线程。
- 打印任务管理:打印队列用于管理多个打印任务的顺序。
- 消息队列:在消息传递系统中,队列用于处理消息的发送和接收。
- 广度优先搜索(BFS) :在图的广度优先搜索算法中使用队列来管理待访问的节点。
当然这些都是最基本的队列还有优先队列、双端队列、循环队列和单调队列
1. 优先队列(Priority Queue)
概念: 优先队列是一种特殊类型的队列,其中每个元素都有一个优先级。元素按照优先级顺序被移除,而不是按照插入的顺序。具有高优先级的元素在队列中排在前面,优先被处理。
实现方式:
- 堆:通常使用二叉堆(最小堆或最大堆)来实现优先队列。
- 有序链表:可以使用有序链表来实现优先队列,但性能较差。
应用:
- 任务调度:操作系统中的任务调度,优先处理高优先级任务。
- A 搜索算法*:路径搜索算法中,优先处理估计代价较低的节点。
- 事件模拟:事件驱动的模拟系统中,根据事件的优先级处理事件。
class PriorityQueue {
constructor() {
this.items = [];
}
enqueue(element, priority) {
const queueElement = { element, priority };
let added = false;
for (let i = 0; i < this.items.length; i++) {
if (queueElement.priority < this.items[i].priority) {
this.items.splice(i, 0, queueElement);
added = true;
break;
}
}
if (!added) {
this.items.push(queueElement);
}
}
dequeue() {
if (this.isEmpty()) {
return 'Queue is empty';
}
return this.items.shift().element;
}
peek() {
if (this.isEmpty()) {
return 'Queue is empty';
}
return this.items[0].element;
}
isEmpty() {
return this.items.length === 0;
}
size() {
return this.items.length;
}
}
// 示例
const pq = new PriorityQueue();
pq.enqueue('Task 1', 2);
pq.enqueue('Task 2', 1);
console.log(pq.dequeue()); // 输出: Task 2 (优先级最高)
console.log(pq.peek()); // 输出: Task 1
2. 双端队列(Deque, Double-ended Queue)
概念: 双端队列是一种可以在两端进行插入和删除操作的数据结构。相比于单端队列,双端队列提供了更多的灵活性。
操作:
- 从前端插入/删除(
addFirst,removeFirst) - 从后端插入/删除(
addLast,removeLast)
应用:
- 缓存机制:双端队列常用于实现双端缓存(如 LRU 缓存)策略。
- 回溯算法:可以用于回溯算法中的路径跟踪。
class Deque {
constructor() {
this.items = [];
}
addFirst(element) {
this.items.unshift(element);
}
addLast(element) {
this.items.push(element);
}
removeFirst() {
if (this.isEmpty()) {
return 'Deque is empty';
}
return this.items.shift();
}
removeLast() {
if (this.isEmpty()) {
return 'Deque is empty';
}
return this.items.pop();
}
peekFirst() {
if (this.isEmpty()) {
return 'Deque is empty';
}
return this.items[0];
}
peekLast() {
if (this.isEmpty()) {
return 'Deque is empty';
}
return this.items[this.items.length - 1];
}
isEmpty() {
return this.items.length === 0;
}
size() {
return this.items.length;
}
}
// 示例
const deque = new Deque();
deque.addLast(1);
deque.addFirst(2);
console.log(deque.peekFirst()); // 输出: 2
console.log(deque.peekLast()); // 输出: 1
console.log(deque.removeLast()); // 输出: 1
console.log(deque.removeFirst()); // 输出: 2
3. 循环队列(Circular Queue)
概念: 循环队列是一种优化的队列实现,其结构呈环状。即当队列的末尾达到了数组的末尾时,新的元素会从数组的开头位置插入,形成一个环。这种结构有效利用了内存空间。
操作:
- 入队(
enqueue) - 出队(
dequeue) - 检查是否为空(
isEmpty) - 检查是否已满(
isFull)
应用:
- 缓冲区:如计算机网络中的数据包缓冲区。
- 任务调度:在循环队列中处理任务轮转。
class CircularQueue {
constructor(capacity) {
this.queue = new Array(capacity);
this.front = 0;
this.rear = 0;
this.size = 0;
this.capacity = capacity;
}
enqueue(element) {
if (this.isFull()) {
return 'Queue is full';
}
this.queue[this.rear] = element;
this.rear = (this.rear + 1) % this.capacity;
this.size++;
}
dequeue() {
if (this.isEmpty()) {
return 'Queue is empty';
}
const element = this.queue[this.front];
this.front = (this.front + 1) % this.capacity;
this.size--;
return element;
}
isEmpty() {
return this.size === 0;
}
isFull() {
return this.size === this.capacity;
}
peek() {
if (this.isEmpty()) {
return 'Queue is empty';
}
return this.queue[this.front];
}
}
// 示例
const cQueue = new CircularQueue(3);
cQueue.enqueue(1);
cQueue.enqueue(2);
cQueue.enqueue(3);
console.log(cQueue.enqueue(4)); // 输出: Queue is full
console.log(cQueue.dequeue()); // 输出: 1
cQueue.enqueue(4);
console.log(cQueue.peek()); // 输出: 2
4. 单调队列(Monotonic Queue)
概念: 单调队列是一种特殊的队列,队列中的元素按照某种顺序(递增或递减)排列。常用于解决滑动窗口最小值/最大值问题。
操作:
- 插入元素:维护队列的单调性。
- 删除元素:在窗口滑动时删除不符合要求的元素。
应用:
- 滑动窗口问题:在给定的窗口中找到最小值或最大值。
class MonotonicQueue {
constructor() {
this.deque = [];
}
push(value) {
while (this.deque.length && this.deque[this.deque.length - 1] < value) {
this.deque.pop();
}
this.deque.push(value);
}
pop(value) {
if (this.deque[0] === value) {
this.deque.shift();
}
}
max() {
return this.deque[0];
}
}
// 示例
const mQueue = new MonotonicQueue();
mQueue.push(1);
mQueue.push(3);
mQueue.push(2);
console.log(mQueue.max()); // 输出: 3
mQueue.pop(3);
console.log(mQueue.max()); // 输出: 2
结束
明天定一个小flag :Leetcode刷十道算法,去把现在碰到的数据结构都敲上一遍,特殊链表,特殊队列这些。