面试连环问:链表和数组,哪个实现队列更快?

99 阅读1分钟

分析

  • 数组是连续存储,push很快,shift很慢
  • 链表是非连续存储,add和delete都很快(但查找很慢)
  • 结论:链表实现队列更快

链表实现队列

  • 单向链表,但同时要记录head和tail
  • 要从tail入队,要从head出队,否则出队时tail不好定位
  • length要实时记录,不可遍历链表获取
interface ILinkNode {
  value: number
  next: ILinkNode | null
}

class MyQueue {
  private head: ILinkNode | null = null
  private tail: ILinkNode | null = null
  private len: number = 0
  add(n: number) {
    const newNode: ILinkNode = {
      value: n,
      next: null,
    }
    // 如果链表为空
    if (this.head === null) {
      this.head = newNode
    }
    // 链表尾部永远指向新节点
    const tailNode = this.tail
    if (tailNode) {
      tailNode.next = newNode
    }
    this.tail = newNode
    this.len++
  }

  delete(): number | null {
    const headNode = this.head;
    if (headNode==null) return null
    if (this.len <= 0) return null
    // 取值
    let value: number = headNode.value;
    // 处理head
    this.head = headNode.next;
    // 记录长度
    this.len--;
    return value;
  }
  get length():number {
    return this.len
  }
}

// 功能测试
const q = new MyQueue()
q.add(100)
q.add(200)
q.add(300)

console.log('length1', q.length);
console.log(q.delete());
console.log('length2', q.length)
console.log(q.delete())
console.log('length3', q.length)
console.log(q.delete())
console.log('length4', q.length)
console.log(q.delete())
console.log('length5', q.length)

// 性能测试
const q1 = new MyQueue()
console.time('queue with list');
for (let i = 0; i < 10 * 10000; i++){
  q1.add(i)
}
for (let i = 0; i < 10 * 10000; i++){
  q1.delete()
}
console.timeEnd('queue with list') // 8ms

const q2:number[] = []
console.time('queue with array')
for (let i = 0; i < 10 * 10000; i++){
  q2.push(i)
}
for (let i = 0; i < 10 * 10000; i++){
  q2.shift()
}
console.timeEnd('queue with array') // 921ms

image.png

性能分析

  • 空间复杂度都是O(n)
  • add 时间复杂度:链表O(1); 数组O(1)
  • delete 时间复杂度:链表O(1); 数组O(n)

结论

链表实现队列速度更快!

数据结构的选择,要比算法优化更重要

要有时间复杂度的敏感性,如length不能遍历查找