JS 之 常见线性结构 - 队列结构

JS 之 常见线性结构 - 队列结构_第1张图片

一、队列及其特性

1. 概念

JS 之 常见线性结构 - 队列结构_第2张图片

2. 常见操作

  • enqueue(element) :向队列尾部添加一个(或多个)新的项

  • dequeue():移除队列的第一(即排在队列最前面的)项,并返回被移除的元素

  • front/peek():返回队列中第一个元素——最先被添加,也将是最先被移除的元素

  • 队列不做任何变动(不移除元素,只返回元素信息——与Stack类的peek方法非常类似)

  • isEmpty():如果队列中不包含任何元素,返回true,否则返回false

  • size():返回队列包含的元素个数,与数组的length属性类似

二、代码实现

0 - 定义队列接口

interface IQueue {
  // 入队
  enqueue(item: T): void;
  // 出队
  dequeue(): T | undefined;
  // 看栈顶元素
  peek(): T | undefined;
  // 栈个数
  size(): number;
  // 是否为空
  isEmpty(): boolean;
}

1. 队列结构实现 - 数组

import IQueue from './IQueue';

class ArrayQueue implements IQueue {
  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();

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 之 常见线性结构 - 队列结构_第3张图片

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();
  // 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)); 

四、面试题- 约瑟夫环问题

JS 之 常见线性结构 - 队列结构_第4张图片
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

五、面试题 - 用两个栈实现队列

JS 之 常见线性结构 - 队列结构_第5张图片
/**
 * 解题思路 : 当调用出栈的时候,出栈清空,入栈的所有元素倒入出栈,把最后一个元素删除
 */
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()!;
  }
}

你可能感兴趣的:(数据结构与算法,数据结构,队列,栈)