数据结构-学习笔记整合
什么是队列
队列是一种数据结构,队列也被称为"等待线",正如名字所暗示的,他们很容易被想象成一群排队等待的人。排队时,排队越早,优先级越高。将数据添加到队列时,数据放在最后。从队列中取出数据时,将从最早添加的数据开始取出。从队列中取出数据的操作称为"出队"。队列是遵循先进先出(FIFO,也称为先来先服务)原则的一组有序的项。队列在尾部添加新元素,并从顶部移除元素。最新添加的元素必须排在队列的末尾。
现实中队列:排队、打印机的打印队列
队列
代码实现
/**
* 队列是遵循先进先出(FIFO,也称为先来先服务)原则的一组有序的项。队列在尾部添加新元素,并从顶部移除元素。最新添加的元素必须排在队列的末尾。
* 生活中常见的队列:排队、打印机的打印队列
*/
export class Queue {
constructor() {
this.count = 0; // 由于使用了对象,使用count属性控制队列的大小。
this.lowestCount = 0; // 由于需要从队列前端移除元素,因此需要lowestCount来帮助追踪第一个元素
this.items = {}; // 可以使用数组,也可以使用对象等数据结构,对象实现队列大多数情况下的算法复杂度优于数组
}
/**
* 向队列尾部添加一个(或多个)新的项。
* @param {*} element 需要添加的项
*/
enqueue(element) {
this.items[this.count] = element;
this.count++;
}
/**
* 移除队列的第一项(即排在队列最前面的项)并返回被移除的元素。
* @returns {*} 被移除的元素
*/
dequeue() {
if (this.isEmpty()) { // 检验队列是否为空
return undefined; // 如果为空,就返回 undefined
}
// 如果队列不为空
const result = this.items[this.lowestCount]; // 暂存队列头部的值,用于返回
delete this.items[this.lowestCount]; // 删除队列头部的元素
this.lowestCount++; // 由于队列头部元素被移除后已不存在,记录队列头部元素指针的属性递增
return result; // 返回被移除的元素
}
/**
* 返回队列中第一个元素——最先被添加,也将是最先被移除的元素。队列不做任何变动(不移除元素,只返回元素信息——与 Stack 类的 peek 方法非常类似)。该方法在其他语言中也可以叫作 front 方法。
* @returns {*} 被移除的元素
*/
peek() {
if (this.isEmpty()) { // 检验队列是否为空
return undefined; // 如果为空,就返回 undefined
}
// 如果队列不为空
return this.items[this.lowestCount]; // 返回队列中第一个元素
}
/**
* 如果队列中不包含任何元素,返回 true,否则返回 false。
* @returns {Boolean}
*/
isEmpty() {
return this.size() === 0;
}
/**
* 清空队列
*/
clear() {
this.items = {};
this.count = 0;
this.lowestCount = 0;
}
/**
* 返回队列包含的元素个数,与数组的 length 属性类似。
* @returns {Number} 队列里的元素个数
*/
size() {
return this.count - this.lowestCount;
}
/**
* 以字符串的方式输出队列的值
* @returns {Array}
*/
toString() {
if (this.isEmpty()) { // 检验队列是否为空
return ''; // 如果为空,就返回 空字符串
}
// 如果队列不为空
let objString = `${this.items[this.lowestCount]}`; // 用队列中第一个元素作为字符串的初始值
//由于 Queue 类中的第一个索引值不一定是 0,我们需要从索引值为 lowestCount 的位置开始迭代队列
for (let i = this.lowestCount + 1; i < this.count; i++) {
// 添加一个逗号(,)以及下一个元素
objString = `${objString},${this.items[i]}`;
}
// 以字符串的方式输出队列的值
return objString;
}
}
双端队列
概念
双端队列(deque,或称 double-ended queue)是一种允许我们同时从前端和后端添加和移除元素的特殊队列。
生活中常见的双端队列
双端队列在现实生活中的例子有电影院、餐厅中排队的队伍等。举个例子,一个刚买了票的人如果只是还需要再问一些简单的信息,就可以直接回到队伍的头部。另外,在队伍末尾的人如果赶时间,他可以直接离开队伍。
在计算机科学中,双端队列的一个常见应用是存储一系列的撤销操作。每当用户在软件中进行了一个操作,该操作会被存在一个双端队列中(就像在一个栈里)。当用户点击撤销按钮时,该操作会被从双端队列中弹出,表示它被从后面移除了。在进行了预先定义的一定数量的操作后,
最先进行的操作会被从双端队列的前端移除。由于双端队列同时遵守了先进先出和后进先出原则,可以说它是把队列和栈相结合的一种数据结构。
代码实现
/**
* 双端队列(deque,或称 double-ended queue)是一种允许我们同时从前端和后端添加和移除元素的特殊队列。
* 生活中常见的双端队列:
* 双端队列在现实生活中的例子有电影院、餐厅中排队的队伍等。举个例子,一个刚买了票的人如果只是还需要再问一些简单的信息,就可以直接回到队伍的头部。另外,在队伍末尾的人如果赶时间,他可以直接离开队伍。
* 在计算机科学中,双端队列的一个常见应用是存储一系列的撤销操作。每当用户在软件中进行了一个操作,该操作会被存在一个双端队列中(就像在一个栈里)。当用户点击撤销按钮时,该操作会被从双端队列中弹出,表示它被从后面移除了。在进行了预先定义的一定数量的操作后,
* 最先进行的操作会被从双端队列的前端移除。由于双端队列同时遵守了先进先出和后进先出原则,可以说它是把队列和栈相结合的一种数据结构。
*/
export class Deque {
constructor() {
this.count = 0; // 由于使用了对象,使用count属性控制双端队列的大小。
this.lowestCount = 0; // 由于需要从双端队列前端移除元素,因此需要lowestCount来帮助追踪第一个元素
this.items = {}; // 可以使用数组,也可以使用对象等数据结构,对象实现双端大多数情况下的算法复杂度优于数组
}
/**
* 在双端队列前端添加新的元素
* @param {*} element 需要添加的项
*/
addFront(element) {
if (this.isEmpty()) { // 如果双端队列是空的
this.addBack(element); // 添加到双端队列的后端
} else if (this.lowestCount > 0) { // 如果一个元素已经被从双端队列的前端移除
this.lowestCount--; // lowestCount 属性会大于等于 1,因此递减lowestCount
this.items[this.lowestCount] = element; // 并将新元素的值放在这个键的位置上即可
} else {
// 我们可以设置一个负值的键,同时更新用于计算双端队列长度的逻辑,使其也能包含负键值。这种情况下,添加一个新元素的操作仍然能保持最低的计算成本。
// 这里仍然把它作为数组来操作
// 要在第一位添加一个新元素,我们需要将所有元素后移一位来空出第一个位置
for (let i = this.count; i > 0; i--) {
this.items[i] = this.items[i - 1];
}
this.count++; // 递增双端队列的大小
this.items[0] = element; // 在所有的元素都完成移动后,第一位将是空闲状态,设置双端队列的第一个元素为新添加的元素
}
}
/**
* 在双端队列后端添加新的元素(实现方法和 Queue 类中的enqueue方法相同)
* @param {*} element 需要添加的项
*/
addBack(element) {
this.items[this.count] = element;
this.count++;
}
/**
* 从双端队列前端移除第一个元素(实现方法和 Queue 类中的dequeue方法相同)
* @returns {*} 被移除的元素
*/
removeFront() {
if (this.isEmpty()) { // 检验双端队列是否为空
return undefined; // 如果为空,就返回 undefined
}
// 如果双端队列不为空
const result = this.items[this.lowestCount]; // 暂存双端队列头部的值,用于返回
delete this.items[this.lowestCount]; // 删除双端队列头部的元素
this.lowestCount++; // 由于双端队列头部元素被移除后已不存在,记录双端队列头部元素指针的属性递增
return result; // 返回被移除的元素
}
/**
* 从双端队列后端移除第一个元素(实现方法和 Stack 类中的pop方法一样)
* @returns {*} 被移除的元素
*/
removeBack() {
if (this.isEmpty()) { // 检验双端队列是否为空
return undefined; // 如果为空,就返回 undefined
}
// 如果双端队列不为空
this.count--; // 双端队列的元素个数减1
const result = this.items[this.count]; // 保存双端队列尾部的值(用于返回)
delete this.items[this.count]; // 删除双端队列尾部的元素
return result; // 返回被移除的元素
}
/**
* 返回双端队列前端的第一个元素(实现方法和Queue类中的 peek方法一样)
* @returns {*} 双端队列前端的第一个元素
*/
peekFront() {
if (this.isEmpty()) { // 检验双端队列是否为空
return undefined; // 如果为空,就返回 undefined
}
// 如果双端队列不为空
return this.items[this.lowestCount]; // 返回双端队列中前端的第一个元素
}
/**
* 返回双端队列后端的第一个元素(实现方法和 Stack 类中的 peek方法一样)
* @returns {*} 双端队列后端的第一个元素
*/
peekBack() {
if (this.isEmpty()) { // 检验双端队列是否为空
return undefined; // 如果为空,就返回 undefined
}
// 如果双端队列不为空
return this.items[this.count - 1]; // 返回双端队列中后端的第一个元素
}
/**
* 如果双端队列中不包含任何元素,返回 true,否则返回 false。
* @returns {Boolean}
*/
isEmpty() {
return this.size() === 0;
}
/**
* 清空双端队列
*/
clear() {
this.items = {};
this.count = 0;
this.lowestCount = 0;
}
/**
* 返回双端队列包含的元素个数,与数组的 length 属性类似。
* @returns {Number} 双端队列里的元素个数
*/
size() {
return this.count - this.lowestCount;
}
/**
* 以字符串的方式输出双端队列的值
* @returns {Array}
*/
toString() {
if (this.isEmpty()) { // 检验双端队列是否为空
return ''; // 如果为空,就返回 空字符串
}
// 如果双端队列不为空
let objString = `${this.items[this.lowestCount]}`; // 用双端队列中第一个元素作为字符串的初始值
//由于 Deque 类中的第一个索引值不一定是 0,我们需要从索引值为 lowestCount 的位置开始迭代队列
for (let i = this.lowestCount + 1; i < this.count; i++) {
// 添加一个逗号(,)以及下一个元素
objString = `${objString},${this.items[i]}`;
}
// 以字符串的方式输出双端队列的值
return objString;
}
}