栈和队列面试篇


写在前面,本文是本人在面试中,面试官问到的一些关于栈和队列的问题,问题的答案是自己总结出来的,可能存在一些偏差,欢迎大家指正
后续如果有碰到更多问题,会继续补充

1. 你知道的线性表有哪些?非线性表呢

线性表:一般线性表、栈与队列、字符串、数组、广义表
非线性表:树、图、集合

2. 简答说一下对栈和队列的了解

栈是先进后出(Last In First Out, LIFO) 队列是先进先出(First In First Out, FIFO)
栈只有两种操作,push(入栈,插入数据)和pop(出栈,删除数据)。用数组实现的栈叫顺序栈,用链表实现的栈叫链式栈
队列:push(入队,插入数据)和pop(出队,删除数据)。用数组实现的栈叫顺序队列,用链表实现的栈叫链式队列

3. 根据你写的代码,说一下入栈、出栈、出队、入队各自的时间复杂度

如下

4. 用数组实现一个栈(顺序栈)

入栈的时间复杂度为O(1),出栈的时间复杂度为O(1)

  class Stack {
    constructor() {
      this.stack = []
    }
    push(item) {
      this.stack.push(item)
    }
    pop() {
      this.stack.pop()
    }
    getTop() {
      return this.stack[this.getCount() - 1]
    }
    getCount() {
      return this.stack.length
    }
    isEmpty() {
      return this.getCount() === 0
    }
  }

5. 用链表实现一个栈(链式栈)

入栈的时间复杂度为O(1),出栈的时间复杂度为O(1)

  function Node(val) {
    this.val = val;
    this.next = null;
  }
  function Stack() {
    this.top = null;
    this.length = 0;
  }
  Stack.prototype.push = function (node) {
    if (!this.top) {
      this.top = node;
      this.length++;
    } else {
      node.next = this.top;
      this.top = node;
      this.length++;
    }
  }
  Stack.prototype.pop = function () {
    let res = this.top || undefined;
    if (this.top) {
      this.top = this.top.next;
      this.length--;
    }
    return res;
  }

6. 用数组实现一个队列(顺序队列)

入队的时间复杂度为O(1),出队的时间复杂度为O(1)

  class Queue {
    constructor() {
      this.queue = []
    }
    push(item) {
      this.queue.push(item)
    }
    pop() {
      this.queue.shift()
    }
    getHeader() {
      return this.queue[0]
    }
    getLength() {
      return this.queue.length
    }
    isEmpty() {
      return this.getLength() === 0
    }
  }

7. 用链表实现一个队列(链式栈)

入队的时间复杂度为O(1),出队的时间复杂度为O(1)

  function Node(val) {
    this.val = val;
    this.next = null;
  }
  function Queue() {
    this.front = null;
    this.tail = null;
    this.length = 0;
  }
  Queue.prototype.push = function (node) {
    if (!this.front) {
      this.front = this.tail = node;
    } else {
      this.tail.next = node;
      this.tail = node;
    }
    this.length++;
  }
  Queue.prototype.pop = function () {
    if (!this.length) return -1;
    let res = this.front;
    this.front = this.front.next;
    this.length--;
    if (!this.length)
      this.tail = null;
    return res;
  }

8. 实现一个循环队列

入队的时间复杂度为O(1),出队的时间复杂度为O(1)

  class SqQueue {
    constructor(length) {
      this.queue = new Array(length + 1)
      this.first = 0
      this.last = 0
      this.size = 0
    }
    enQueue(item) {
      // 判断队尾 + 1 是否为队头
      // 如果是就代表需要扩容数组
      // % this.queue.length 是为了防止数组越界
      if (this.first === (this.last + 1) % this.queue.length) {
        this.resize(this.getLength() * 2 + 1)
      }
      this.queue[this.last] = item
      this.size++
      this.last = (this.last + 1) % this.queue.length
    }
    deQueue() {
      if (this.isEmpty()) {
        throw Error('Queue is empty')
      }
      let r = this.queue[this.first]
      this.queue[this.first] = null
      this.first = (this.first + 1) % this.queue.length
      this.size--
      // 判断当前队列大小是否过小
      // 为了保证不浪费空间,在队列空间等于总长度四分之一时
      // 且不为 2 时缩小总长度为当前的一半
      if (this.size === this.getLength() / 4 && this.getLength() / 2 !== 0) {
        this.resize(this.getLength() / 2)
      }
      return r
    }
    getHeader() {
      if (this.isEmpty()) {
        throw Error('Queue is empty')
      }
      return this.queue[this.first]
    }
    getLength() {
      return this.queue.length - 1
    }
    isEmpty() {
      return this.first === this.last
    }
    resize(length) {
      let q = new Array(length)
      for (let i = 0; i < length; i++) {
        q[i] = this.queue[(i + this.first) % this.queue.length]
      }
      this.queue = q
      this.first = 0
      this.last = this.size
    }
  }

9. 两个栈实现一个队列

堆栈A和堆栈B,当有元素要插入的时候,就往堆栈A里插入。当要移除元素的时候,先将堆栈A里的元素依次出栈放入到堆栈B中,再从堆栈B的顶部出数据。如此便基于2个堆栈实现了先进先出的原则了
入队的时间复杂度为O(1),出队的时间复杂度为O(n)

  class MyQueue {
    constructor() {
      this.s1 = []
      this.s2 = []
      this.front = null
    }

    enPush(item) {
      this.s1.push(item)
    }

    dePop() {
      if (this.s2.length === 0) {
        while (this.s1.length !== 0) {
          this.s2.push(this.s1.pop())
        }
      }
      this.front = this.s2[1]
      return this.s2.pop()
    }

    peek() {
      if (this.s2.length === 0) {
        return this.s1[0] ? this.s1[0] : null
      }
      return this.front
    }

    isEmpty() {
      if (this.s1.length === 0 && this.s2.length === 0) {
        return '队列为空'
      }
      return '队列不为空'
    }
  }
  let ans = new MyQueue()
  ans.enPush(2)
  ans.enPush(3)
  console.log(ans.peek());  // 2
  console.log(ans.isEmpty()); // 队列不为空
  console.log(ans);

10. 两个队列实现一个栈

队列A和队列B,当需要进行入栈操作的时候,直接往队列A中插入元素。当需要进行出栈操作的时候,先将队列A中的前n-1个元素依次出队移动到队列B中,这样队列A中剩下的最后一个元素其实就是我们所需要出栈的元素了,将这个元素出队即可
入栈的时间复杂度为O(1),出栈的时间复杂度为O(n)

  class MyStack {
    constructor() {
      this.q1 = []
      this.q2 = []
      this.front = null
    }

    enPush(item) {
      this.q1.push(item);
      this.front = item;
    }

    dePop() {
      while (this.q1.length > 1) {
        this.front = this.q1.shift();
        this.q2.push(this.front);
      }
      // 交换p1和p2
      let val = this.q1.pop();
      let temp = this.q2;
      this.q2 = this.q1;
      this.q1 = temp;
      return val;
    }

    getTop() {
      return this.front;
    }

    isEmpty() {
      return this.q1.length === 0;
    }
  }
  let ans = new MyStack()
  ans.enPush(2)
  ans.enPush(5)
  ans.dePop()
  console.log(ans.getTop()) // 2
  console.log(ans.isEmpty()); // false
  console.log(ans); // MyStack {q1: Array(1), q2: Array(0), front: 2}

这道题其实还有另一个解法,只需要一个队列就可以做到模拟出堆栈,思路就是:当需要进行入栈操作的时候,先将新元素插入到队列的队尾中,再将这个队列中的其它元素依次出队,队列的特性当然是从队头出队了,但是出来的元素再让它们从队尾入队,这样依次进行,留下刚才插入的新元素不动,这个时候,这个新元素其实就被顶到了队头了,新元素入栈的动作就完成了。当需要进行出栈操作的时候,就直接将队列队头元素出队即是了。

11. 实现一个优先队列

优先队列(priority Queue)是一种特殊的队列,它不遵守先进先出的原则,它是按照优先级出队列的。分为最大优先队列(是指最大的元素优先出队)和最小优先队列(是指最小的元素优先出队)。
入队的时间复杂度为O(1),出队的时间复杂度为O(n)

  class priorityQueue {
    constructor() {
      this.queue = []
      this.front = null
      this.back = null
    }
    enQueue(item) {
      this.queue.push(item)
    }
    deQueue() {
      let priority = this.queue[0].code
      let fromIndex = 0
      this.queue.forEach((item, index) => {
        if (item.code < priority) {
          fromIndex = index
        }
      })
      // 弹出优先级最高的元素
      return this.queue.splice(fromIndex, 1)
    }
    getTop() {
      return this.queue[this.queue.length - 1]
    }
    getBack() {
      return this.queue[0]
    }
    isEmpty() {
      if (this.queue.length === 0) {
        return true
      }
      return false
    }
    getCount() {
      return this.queue.length
    }
  }
  class Patient {
    constructor(name, code) {
      this.name = name
      // code代表优先级,越小代表优先级越高
      this.code = code
    }
  }

  let q = new priorityQueue()
  let p1 = new Patient('Smith', 4)
  let p2 = new Patient('Tom', 2)
  let p3 = new Patient('Jane', 6)
  q.enQueue(p1)
  q.enQueue(p2)
  q.enQueue(p3)
  console.log(q.getTop()) // Jane...
  console.log(q.getBack()) // Smith...
  console.log(q.isEmpty()) // false
  console.log(q.deQueue()) // Tom...
  console.log(q.getCount()) // 2

参考链接

栈实现队列、队列实现栈
顺序栈、顺序队列、循环队列
链表实现栈和队列
优先队列

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值