-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathPersistentQueue.ts
149 lines (122 loc) · 3.95 KB
/
PersistentQueue.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* eslint-disable max-len */
/* eslint-disable no-shadow */
// notes:
// !1.注意evalute用 `xx._evaluate =` 重新赋值会比写在构造函数里面快很多;
// !2.由于内部占用了构造函数,因此对外不暴露构造函数,只能通过init方法来创建实例.
import assert from 'assert'
/**
* 完全可持久化队列.均摊时间复杂度O(1),最坏时间复杂度O(N).
* @see https://www.kmonos.net/pub/Presen/PFDS.pdf
* @see https://37zigen.com/bankers-queue/
*/
class PersistentQueue<V> {
static init<V>(): PersistentQueue<V> {
return new PersistentQueue()
}
private readonly _frontSize: number
private readonly _rearSize: number
private readonly _front: _PersistentStack<V> | undefined
private readonly _rear: _PersistentStack<V> | undefined
private constructor(frontSize?: number, rearSize?: number, front?: _PersistentStack<V>, rear?: _PersistentStack<V>) {
this._frontSize = frontSize || 0
this._rearSize = rearSize || 0
this._front = front
this._rear = rear
}
empty(): boolean {
return !this._frontSize
}
top(): V | undefined {
if (!this._front) return undefined
return this._front.top()
}
push(x: V): PersistentQueue<V> {
return new PersistentQueue(this._frontSize, this._rearSize + 1, this._front, _PersistentStack.push(this._rear, x))._normalize()
}
pop(): PersistentQueue<V> {
return new PersistentQueue(this._frontSize - 1, this._rearSize, this._front && this._front.pop(), this._rear)._normalize()
}
length(): number {
return this._frontSize + this._rearSize
}
/** 延迟reverse. */
private _normalize(): PersistentQueue<V> {
if (this._frontSize >= this._rearSize) {
return this
}
return new PersistentQueue(this._frontSize + this._rearSize, 0, _PersistentStack.concat(this._front, _PersistentStack.reverse(this._rear!)))
}
}
class _PersistentStack<V> {
static init<V>(): _PersistentStack<V> {
return new _PersistentStack()
}
static push<V>(x: _PersistentStack<V> | undefined, v: V): _PersistentStack<V> {
return new _PersistentStack(v, x)
}
static concat<V>(x: _PersistentStack<V> | undefined, y: _PersistentStack<V>): _PersistentStack<V> {
if (!x) {
return y._evaluate()
}
const next = new _PersistentStack<V>()
next._evaluate = () => _PersistentStack.concat(x.pop()!, y)
return new _PersistentStack(x._value, next)
}
static reverse<V>(head: _PersistentStack<V>): _PersistentStack<V> {
const res = new _PersistentStack<V>()
res._evaluate = () => {
let tmp: _PersistentStack<V> | undefined
for (let x = head; x; x = x.pop()!) {
tmp = _PersistentStack.push(tmp, x.top()!)
}
return tmp!
}
return res
}
private _next: _PersistentStack<V> | undefined
private readonly _value: V | undefined
private _evaluate: () => _PersistentStack<V>
private constructor(value?: V, next?: _PersistentStack<V>, evaluate?: () => _PersistentStack<V>) {
this._next = next
this._value = value
this._evaluate = evaluate || (() => this)
}
empty(): boolean {
return !this._next
}
top(): V | undefined {
return this._value
}
pop(): _PersistentStack<V> | undefined {
if (this._next) {
this._next = this._next._evaluate()
}
return this._next
}
}
export { PersistentQueue }
if (require.main === module) {
let queue = PersistentQueue.init<number>()
assert(queue.empty())
assert(queue.top() === undefined)
const queue1 = queue.push(1)
assert(!queue1.empty())
assert(queue1.top() === 1)
const queue2 = queue1.push(2)
assert(!queue2.empty())
assert(queue2.top() === 1)
const queue3 = queue2.pop()
assert(!queue3.empty())
assert(queue3.top() === 2)
const queue4 = queue3.pop()
assert(queue4.empty())
assert(queue4.top() === undefined)
console.time('queue')
for (let i = 0; i < 1e7; i++) {
queue = queue.push(i)
queue = queue.pop()
queue.empty()
queue.top()
}
console.timeEnd('queue') // 325.638ms
}