一、队列及其特性
1. 概念
2. 常见操作
enqueue(element) :向队列尾部添加一个(或多个)新的项
dequeue():移除队列的第一(即排在队列最前面的)项,并返回被移除的元素
front/peek():返回队列中第一个元素——最先被添加,也将是最先被移除的元素
队列不做任何变动(不移除元素,只返回元素信息——与Stack类的peek方法非常类似)
isEmpty():如果队列中不包含任何元素,返回true,否则返回false
size():返回队列包含的元素个数,与数组的length属性类似
二、代码实现
0 - 定义队列接口
interface IQueue<T> {
// 入队
enqueue(item: T): void;
// 出队
dequeue(): T | undefined;
// 看栈顶元素
peek(): T | undefined;
// 栈个数
size(): number;
// 是否为空
isEmpty(): boolean;
}
1. 队列结构实现 - 数组
import IQueue from './IQueue';
class ArrayQueue<T> implements IQueue<T> {
private dataList: T[] = [];
enqueue(item: T): void {
this.dataList.push(item);
}
dequeue(): T | undefined {
return this.dataList.shift();
}
peek(): T | undefined {
return this.dataList[0];
}
size(): number {
return this.dataList.length;
}
isEmpty(): boolean {
return this.dataList.length === 0;
}
}
/**
* 测试代码
*/
const queue = new ArrayQueue<string>();
queue.enqueue('abc');
queue.enqueue('cba');
queue.enqueue('nba');
console.log(queue.dequeue()); // abc
console.log(queue.dequeue()); // cba
console.log(queue.peek()); // nba
console.log(queue.isEmpty()); // false
console.log(queue.size()); // 1
2. 队列结构实现 - 链表
三、面试题 - 击鼓传花
js实现 - 直接使用数组
function hotPotato(list, num) {
const queue = [...list];
while (queue.length > 1) {
for (let i = 1; i < num; i++) {
queue.push(queue.shift());
}
queue.shift();
}
const lastItem = queue.shift();
const lastIndex = list.indexOf(lastItem);
return {
lastItem,
lastIndex
};
}
// { lastItem: 'd', lastIndex: 3 }
console.log(hotPotato(['a', 'b', 'c', 'd', 'e', 'f', 'g'], 3));
// { lastItem: 'cba', lastIndex: 5 }
console.log(hotPotato(['why', 'james', 'kobe', 'curry', 'abc', 'cba', 'nba', 'mba'], 4));
ts实现 - 使用封装队列
/**
*
* @param nameList 击鼓传花的人数
* @param num 每次删除人的位置
* @returns 返回最后删除人原先的位置
*/
function hotPotato(nameList: string[], num: number): Object {
// 1. 创建队列
const queue = new ArrayQueue<string>();
// 2. 把数组元素全加入队列中
for (const item of nameList) {
queue.enqueue(item);
}
// 3. 当队列元素大于1个时,再次循环
while (queue.size() > 1) {
/**
* 比如 num 为 3
* 需要循环两次
* a、b出队后入队
* c出队,不再入队
*/
for (let i = 1; i < num; i++) {
// 4. 当还没有到达删除元素前,把经过的值重新推入队列中,出队后马上入队
queue.enqueue(queue.dequeue()!);
}
// 5. 此时来到要删除的num,移除队列
queue.dequeue()!;
}
// 6. 剩余最后一个名字
const lastItem = queue.dequeue()!;
// 7. 最后一个人原先所在的位置
const lastIndex = nameList.indexOf(lastItem);
// 7. 返回出去
return { lastItem, lastIndex };
}
// 剩下d => 索引为3 { lastItem: 'd', lastIndex: 3 }
console.log(hotPotato(['a', 'b', 'c', 'd', 'e', 'f', 'g'], 3));
四、面试题- 约瑟夫环问题
note : 这里用的是队列的思想,好理解,但是会超时,哦豁
function lastRemaining(n: number, m: number): number {
// 1. 生成数组
const arrList: number[] = new Array(n).fill(0).map((_, index) => index);
// 2. 结束循环条件
while (arrList.length > 1) {
// 3. 到达指定位置前,不删除元素,把经过的元素重新加入队列中
for (let i = 1; i < m; i++) {
arrList.push(arrList.shift()!);
}
// 4. 删除指定位置元素
arrList.shift();
}
// 5. 返回最后存在的元素
return arrList.shift()!;
}
console.log(lastRemaining(5, 3)); // 3
console.log(lastRemaining(10, 17)); // 2
五、面试题 - 用两个栈实现队列
/**
* 解题思路 : 当调用出栈的时候,出栈清空,入栈的所有元素倒入出栈,把最后一个元素删除
*/
class CQueue {
// 定义入栈
private enStack: number[] = [];
// 定义出栈
private deStack: number[] = [];
appendTail(value: number): void {
// 正常入栈
this.enStack.push(value);
}
deleteHead(): number {
// 如果出栈有值,直接返回
if (this.deStack.length) return this.deStack.pop()!;
// 当入栈为空,直接返回-1
if (this.enStack.length === 0) return -1;
// 交换,把入栈的值倒入出栈中
while (this.enStack.length) {
this.deStack.push(this.enStack.pop()!);
}
// 拿到结果
return this.deStack.pop()!;
}
}