算法随笔-数据结构(队列)
本文主要介绍数据结构中的队列的特点、使用场景、ES6 实现 Queue 类和题解 leetCode 真题。供自己以后查漏补缺,也欢迎同道朋友交流学习。
引言
队列
可能是大家最熟悉的数据结构了,日常生活案例也不少,比如排队进地铁闸机口、排队进电影院、排队买票;对于代码而言,也很简单,就是数组里的一堆元素以排队的形式存储,然后按照先进先出的原则取出。
队列是先进先出(FIFO
)的,它的插入(入队
)和删除(出队
)操作分别在队头和队尾进行,因此队列的插入和删除操作都是 O(1) 的时间复杂度。
队列的主要特点
数据结构中的队列是一种特殊的线性表,它具有以下特点:
- 先进先出(FIFO):队列中的元素按照它们被添加的顺序进行处理,即先进入队列的元素先被取出。
- 两端操作:队列的基本操作通常发生在两端,一端是队尾(用于添加元素,称为入队操作),另一端是队头(用于移除元素,称为出队操作)。
- 有序性:队列中的元素保持它们被添加时的顺序,直到它们被取出。
- 限制性访问:只能从队头取出元素,只能从队尾添加元素。
队列的应用场景
- 任务调度:操作系统中的任务调度,按照任务到达的顺序执行。
- 打印任务管理:打印机队列,按照任务提交的顺序打印文档。
- 消息队列:在消息传递系统中,消息按照发送顺序进行处理。
- 缓存系统:在某些缓存系统中,按照请求的顺序处理缓存项。
ES6实现队列类(Queue)
在ES6中,可以使用类(class)来实现一个简单的队列。下面是一个 Queue
类的实现示例:
class Queue {
constructor() {
this.items = []; // 使用数组来存储队列中的元素
}
// 入队操作,将元素添加到队列的末尾
enqueue(element) {
this.items.push(element);
}
// 出队操作,移除队列的第一个元素并返回它
dequeue() {
if (this.isEmpty()) {
throw new Error('Queue is empty');
}
return this.items.shift();
}
// 查看队列的第一个元素
front() {
if (this.isEmpty()) {
throw new Error('Queue is empty');
}
return this.items[0];
}
// 检查队列是否为空
isEmpty() {
return this.items.length === 0;
}
// 获取队列的大小
size() {
return this.items.length;
}
// 清空队列
clear() {
this.items = [];
}
// 打印队列中的元素
print() {
console.log(this.items.toString());
return this.items.toString();
}
}
// 使用Queue类
const queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
queue.print(); // 输出: 1,2,3
console.log(queue.dequeue()); // 输出: 1
queue.print(); // 输出: 2,3
在这个Queue
类中,我们定义了以下方法:
enqueue(element)
: 使用数组的push
方法进行入队操作,将元素添加到队列的末尾。dequeue()
: 使用数组的shift
方法进行出队操作,移除队列的第一个元素并返回它。front()
: 查看队列的第一个元素。isEmpty()
: 检查队列是否为空。size()
: 获取队列的大小。clear()
: 清空队列。print()
: 打印队列中的元素。
LeetCode真题
387. 字符串中的第一个唯一字符
给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。
示例 1:
输入: s = "leetcode"
输出: 0
示例 2:
输入: s = "loveleetcode"
输出: 2
示例 3:
输入: s = "aabb"
输出: -1
题解:
使用队列的入队和出队去做,参考如下:
/**
* @param {string} s
* @return {number}
*/
var firstUniqChar = function(s) {
// 出队队列
var queue = s.split('');
// 入队队列
var queue2 = [];
// 存储第一个不重复的字符串
var char = ''
while (true) {
// 如果队列为空, 跳出循环
if (!queue.length) {
break;
}
// 先执行出队
const cur = queue.shift()
// 判断出队队列中没有该字符 且 入队队列也没有该字符
if (!queue.includes(cur) && !queue2.includes(cur)) {
char = cur
break;
}
// 入队
queue2.push(cur)
}
return char === '' ? -1 : s.indexOf(char)
};
当然本题用队列去做,比较复杂了,下面是个更简单的,内存占用更小的:
var firstUniqChar = function(s) {
for (let i = 0; i < s.length; i++) {
if (s.indexOf(s[i]) === s.lastIndexOf(s[i])) {
return i;
}
}
return -1
};
1823. 找出游戏的获胜者
共有 n
名小伙伴一起做游戏。小伙伴们围成一圈,按 顺时针顺序 从 1 到 n
编号。确切地说,从第 i
名小伙伴顺时针移动一位会到达第 (i+1
) 名小伙伴的位置,其中 1 <= i < n
,从第 n
名小伙伴顺时针移动一位会回到第 1 名小伙伴的位置。
游戏遵循如下规则:
- 从第
1
名小伙伴所在位置 开始 。 - 沿着顺时针方向数
k
名小伙伴,计数时需要包含
起始时的那位小伙伴。逐个绕圈进行计数,一些小伙伴可能会被数过不止一次。 - 你数到的最后一名小伙伴需要离开圈子,并视作输掉游戏。
- 如果圈子中仍然有不止一名小伙伴,从刚刚输掉的小伙伴的 顺时针下一位 小伙伴 开始,回到步骤
2
继续执行。 - 否则,圈子中最后一名小伙伴赢得游戏。
给你参与游戏的小伙伴总数 n
,和一个整数 k
,返回游戏的获胜者。
输入:n = 5, k = 2
输出:3
解释:游戏运行步骤如下:
从小伙伴 1 开始。
顺时针数 2 名小伙伴,也就是小伙伴 1 和 2 。
小伙伴 2 离开圈子。下一次从小伙伴 3 开始。
顺时针数 2 名小伙伴,也就是小伙伴 3 和 4 。
小伙伴 4 离开圈子。下一次从小伙伴 5 开始。
顺时针数 2 名小伙伴,也就是小伙伴 5 和 1 。
小伙伴 1 离开圈子。下一次从小伙伴 3 开始。
顺时针数 2 名小伙伴,也就是小伙伴 3 和 5 。
小伙伴 5 离开圈子。只剩下小伙伴 3 。所以小伙伴 3 是游戏的获胜者。
题解:
/**
* @param {number} n
* @param {number} k
* @return {number}
*/
var findTheWinner = function (n, k) {
// 生成数组,原始队列
const queue = [];
for (let i = 1; i <= n; i++) {
queue.push(i);
}
// 循环队列
while (queue.length > 1) {
// 循环入队
for (let i = 1; i < k; i++) {
console.log(queue);
console.log(i);
// 把之前出队的入队到
queue.push(queue.shift());
}
// 出队
queue.shift();
}
return queue[0];
};