第二节:队列详解和面试题剖析
一. 队列详解
1. 什么是队列?
它是一种受限的线性表,先进先出(FIFO First In First Out)
受限之处在于它只允许在队列的前端(front)进行删除操作;而在队列的后端(rear)进行插入操作
2. 队列常见的操作
enqueue(ele) :向队列尾部添加一个(或多个)新的项。
dequeue():移除队列的第一(即排在队列最前面的)项,并返回被移除的元素。
front/peek():返回队列中第一个元素——最先被添加,也将是最先被移除的元素。队列不做任何变动(不移除元素,只返回元素信息——与Stack类的peek方法非常类似)。
isEmpty():如果队列中不包含任何元素,返回true,否则返回false。
size():返回队列包含的元素个数,与数组的length属性类似。
二. 手写队列
1. 分析
队列和栈一样,可以基于数组实现,也可以基于队列实现。
使用泛型类、泛型接口,来编写队列。
2. 实操--基于数组实现
主要用到数组的两个方法: push:从数组尾部插入元素 shift:删除数组的第一个元素,并返回该元素。
(1). 抽离公共泛型接口IList,可服务于队列和栈
interface IList<T> {
peek(): T | undefined;
isEmpty(): Boolean;
size(): number;
}
export default IList;
(2). 定义泛型队列接口IQueue,继承IList
interface IQueue<T> extends IList<T> {
enqueue(item: T): void;
dequeue(): T | undefined;
// 下面代码注释,继承IList中的即可
// peek(): T | undefined;
// isEmpty(): Boolean;
// size(): number;
}
export default IQueue;
(3). 定义泛型队列MyQueue,实现IQueue接口
class MyQueue<T> implements IQueue<T> {
//底层数据结构,泛型数组
private array: T[] = [];
//入队
enqueue(item: T): void {
this.array.push(item);
}
//出队,并返回出队元素
dequeue(): T | undefined {
return this.array.shift();
}
// 返回队首元素,不做任何操作
peek(): T | undefined {
return this.array[0];
}
//判断队列是否为空
isEmpty(): Boolean {
return this.array.length === 0;
}
//返回队列元素个数
size(): number {
return this.array.length;
}
//返回队列元素个数(以属性的形式存在)
get size2(): number {
return this.array.length;
}
}
export default MyQueue;
(4). 测试
import MyQueue from "./01-实现队列结构";
let queue = new MyQueue();
// 入队
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
queue.enqueue(4);
queue.enqueue(5);
console.log(queue.size()); //函数调用
console.log(queue.size2); //属性调用
console.log(queue.isEmpty()); //false
console.log(queue.peek()); //1
//出队
console.log("-------------开始出队--------------");
/*
for循环:用于已知个数的情况
while循环:用于未知个数的情况
*/
while (queue.size2 > 0) {
console.log(queue.dequeue());
}
三. 面试题剖析
1. 击鼓传花
(1). 规则说明
(2). 剖析
* A. 首先需要将数组中全部入队
* B. 先以第一次数数为例,实际上就是一个for循环,num之前,直接出队,入队;到了num之后,那么就淘汰,该次数数彻底结束
* C. 最外层加个while,相当于多次数数,最后剩一个元素的时候停止。
(3). 实操
import MyQueue from "./01-实现队列结构";
/**
* 击鼓传花方案实操
* @param array 小朋友名字数组
* @param num 数字num代表停止,该小朋友淘汰
* @returns 最后剩下的小朋友的名字
*/
function hotPotato(array: string[], num: number): string {
//0. 前置判断
if (array.length === 0) return "数组不能为空";
//1. 声明队列
let queue = new MyQueue<string>();
//2. 所有小朋友入队
array.forEach(item => queue.enqueue(item));
//3. 遍历队列,进行出队、入队/淘汰,直到剩下一个小朋友为止
while (queue.size() > 1) {
//3.1 不淘汰,重新入队
for (let i = 1; i < num; i++) {
//先出队,然后入队
const item = queue.dequeue();
if (item) queue.enqueue(item);
}
//3.2 直接淘汰
queue.dequeue(); // 这个就相当于第num个元素,直接淘汰
}
return queue.peek()!;
}
//调用
const nameList = ["ypf1", "ypf2", "ypf3", "ypf4", "ypf5"];
const result = hotPotato(nameList, 3); //ypf3
const index = nameList.indexOf(result); //3
console.log(`最后剩下的小朋友名字为:${result},所在的位置:${index}`);
2. 约瑟夫环的问题
(1). 题目描述
( leetcode: https://leetcode.cn/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/ )
0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3
(2). 剖析
(3). 实操
import MyQueue from "./01-实现队列结构";
/**
* 约瑟夫环问题
* @param n 表示n个数字,注:从0开始,所以最后一个数为n-1
* @param m 第m个数字删除
* @returns 剩下的最后一个数字
*/
function leftRemaining(n: number, m: number): number {
//1. 声明队列
let queue = new MyQueue<number>();
//2. 入队
for (let i = 0; i < n; i++) {
queue.enqueue(i);
}
//3. 遍历操作
while (queue.size2 > 1) {
//3.1 不淘汰
for (let h = 1; h < m; h++) {
queue.enqueue(queue.dequeue()!);
}
//3.2 淘汰
queue.dequeue();
}
return queue.dequeue()!;
}
// 调用
console.log(leftRemaining(7, 4)); //1
console.log(leftRemaining(12, 5)); //0
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
2021-11-09 第四节:TypeScipt类、接口、枚举类型详解
2020-11-09 第十一节:Redis6.0新特性、剖析线程模型(单线程和多线程)
2019-11-09 第十三节:数据库索引相关和EFCore的索引映射