Q17~Q20 栈和队列相关-1

56 阅读4分钟

Q17- code225-用队列实现栈

实现思路

方法1.1: 双队列- 易push难pop

1 双队列:关键是对最后一个元素进行特殊处理:排除 && 返回其值

方法1.2: 双队列 + topV- 易push和top,难pop

1 在 双队列基础上,额外通过一个变量,来记录栈顶元素,从而减少top耗时

方法2: 单队列 + 弹出后弹入- 难push,易pop

1 关键在于 添加x--> 弹出--> 在加入的 操作逻辑步骤

参考文档

01- 方法1~2参考实现

代码实现

1 方法1.1: 双队列- 易push难pop 时间复杂度: O(n) 空间复杂度: O(1)

// 方法1.1
class MyStack {
  queue: Array<number>;
  constructor() {
    this.queue = [];
  }

  push(x: number): void {
    this.queue.push(x);
  }

  pop(): number {
    // 第2个临时q2:
    // 除最后一个元素,都按序移到p2--> 移除最后一个元素--> 把p2移回p1

    if (this.empty()) return;

    let q2 = [];
    // 除最后一个元素,都按序移到p2
    while (this.queue.length > 1) {
      q2.push(this.queue.shift());
    }

    // 移除最后一个元素
    let lastV = this.queue.shift();

    // 把p2移回p1
    while (q2.length) {
      this.queue.push(q2.shift());
    }

    return lastV;
  }

  top(): number {
    // 直接复用pop即可
    const lastV = this.pop();
    this.push(lastV);
    return lastV;
  }

  empty(): boolean {
    return this.queue.length === 0;
  }
}

2 方法1.2: 双队列 + topV- 易push和top,难pop 时间复杂度: O(n) 空间复杂度: O(1)

class MyStack {
  queue: Array<number>;
  topV: null | number
  
  constructor() {
    this.queue = [];
    this.topV = null
  }

  push(x: number): void {
    this.queue.push(x);
    this.topV = x
  }

  pop(): number {
    // 第2个临时q2:
    // 除最后一个元素,都按序移到p2--> 移除最后一个元素--> 把p2移回p1

    if (this.empty()) return;

    let q2 = [];
    // 除最后一个元素,都按序移到p2
    while (this.queue.length > 1) {
      q2.push(this.queue.shift());
    }

    // 移除最后一个元素
    let lastV = this.queue.shift();

    // 把p2移回p1, 通过this.push来 自动跟新lastV的值
    while (q2.length) {
      this.push(q2.shift());
    }

    return lastV;
  }

  top(): number {
    // 直接返回 topV即可
    return this.topV;
  }

  empty(): boolean {
    return this.queue.length === 0;
  }
}

3 方法2: 单队列 + 弹出后弹入- 难push,易pop 时间复杂度: O(n) 空间复杂度: O(1)

class MyStack {
  queue: Array<number>;

  constructor() {
    this.queue = [];
  }

  push(x: number): void {
    // 记录添加x前的 队列已有内容数量--> 加入x--> 弹出x之前的内容 & 重新推入x之前的内容
    let size = this.queue.length;
    this.queue.push(x);
    while (size > 0) {
      this.queue.push(this.queue.shift());
      size--;
    }
  }

  pop(): number {
    return this.queue.shift();
  }

  top(): number {
    return this.queue[0];
  }

  empty(): boolean {
    return this.queue.length === 0;
  }
}

Q18- code346-从数据流中移动平均值

实现思路

方法1.1:

1 节省时间的技巧:通过队列 + sum变量,来记录已有的sum值,从而简化求和操作

参考文档

01- 方法1参考实现

代码实现

1 方法1.1: 队列 时间复杂度: O(1) 空间复杂度: O(n)

class MovingAverage {
  private size: number;
  private queue: number[] = [];
  private sum: number;

  constructor(size: number) {
    this.size = size;
    this.queue = [];
    this.sum = 0;
  }

  next(val: number): number {
    if (this.queue.length === this.size) {
      let x = this.queue.shift();
      this.sum -= x;
    }
    this.queue.push(val);
    this.sum += val;
    // 返回结果
    return this.sum / this.queue.length;
  }

}

Q19- code281-之字形迭代器

实现思路

方法1.1:

1 队列记录的结构是一个自定义对象: Array< { arr: number[]; idx: number } >

2 通过这个自定义对象里的idx,配合队列的push+shift,就能实现 k个队列的读取操作

参考文档

01- 方法1参考实现

代码实现

1 方法1.1: 队列 & 当前索引定位 时间复杂度: O(n) 空间复杂度: O(1)

class ZigzagIterator {
  private queue: Array<{
    arr: number[];
    idx: number;
  }>;

  constructor(v1: number[], v2: number[]) {
    this.queue = [];
    if (v1.length > 0) this.queue.push({ arr: v1, idx: 0 });
    if (v2.length > 0) this.queue.push({ arr: v2, idx: 0 });
  }

  // 支持是k个数组的情况:修改构造函数,接收数组的数组
  // constructor(vectors: number[][]) {
  //   this.queue = [];
  //   // 将所有非空数组加入队列
  //   for (const arr of vectors) {
  //     if (arr.length > 0) {
  //       this.queue.push({ arr, index: 0 });
  //     }
  //   }
  // }

  next(): number {
    if (!this.hasNext()) return -1;
    const { arr, idx } = this.queue.shift();
    const val = arr[idx];
    if (idx + 1 < arr.length) {
      this.queue.push({ arr, idx: idx + 1 });
    }
    return val;
  }

  hasNext(): boolean {
    return this.queue.length > 0;
  }
}

Q20 code1429-第一个唯一数字

实现思路

方法1: HashMap + Queue

1 通过 Map记录 当前数字是否唯一

2.1 通过 queue 来存储所有唯一值

2.2 通过 qIdx 来记录第一个唯一值的位置,从而避免频繁出栈+查询

参考文档

01- 方法1参考实现

代码实现

1 方法1: HashMap + Queue

时间复杂度:

  • init: O(n)
  • add: O(n), 均摊时间复杂度: O(1)
  • showFirstUnique: O(1)

空间复杂度: O(n)

class FirstUnique {
  private uniqueMap: Map<number, Boolean> = new Map()
  private q: number[] = []
  private qIdx: number = 0
 
  constructor(nums: number[]) {
    for (let val of nums) {
      this.add(val)
    }
  }

  add(value: number): void {
    // 记录当前数字value 是否唯一
    if (!this.uniqueMap.has(value)) {
      this.uniqueMap.set(value, true)
    } else {
      this.uniqueMap.set(value, false)
    }

    // 如果当前数字value 是唯一的,则加入队列
    if (this.uniqueMap.get(value)) {
      this.q.push(value)
    }

    // 把 qIdx 指向 队列中的第一个 唯一数字
    while (this.qIdx < this.q.length &&
    !this.uniqueMap.get(this.q[this.qIdx])) {
      this.qIdx++
    }
  }

  showFirstUnique(): number {
    if (this.qIdx < this.q.length) {
      return this.q[this.qIdx]
    }
    return -1
  }

}