分析
- 数组是连续存储,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
性能分析
- 空间复杂度都是O(n)
- add 时间复杂度:链表O(1); 数组O(1)
- delete 时间复杂度:链表O(1); 数组O(n)
结论
链表实现队列速度更快!
数据结构的选择,要比算法优化更重要
要有时间复杂度的敏感性,如length不能遍历查找