-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathTreeIterator.ts
166 lines (138 loc) · 4.01 KB
/
TreeIterator.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/* eslint-disable no-lone-blocks */
/**
* 返回要遍历的下一个子节点.
* 如果没有更多子节点, 返回 undefined.
*/
type NextChildFunc<N> = () => N | undefined
interface ITreeIterator<N> {
hasNext(): boolean
next(): N | undefined
}
interface IOperations<N> {
getNextChildFunc(node: N, reverse: boolean): NextChildFunc<N>
}
interface ITraverseOptions<N> {
reverse: boolean
filter: (node: N) => boolean
}
class Context<N> {
private readonly _nextChild: NextChildFunc<N>
constructor(nextChild: NextChildFunc<N>) {
this._nextChild = nextChild
}
next(): N | undefined {
return this._nextChild()
}
}
class State<N> {
private readonly _stacks: Context<N>[] = []
push(context: Context<N>): void {
this._stacks.push(context)
}
pop(): void {
this._stacks.pop()
}
current(): Context<N> | undefined {
return this._stacks.length ? this._stacks[this._stacks.length - 1] : undefined
}
}
class TreeIterator<N> implements ITreeIterator<N> {
private readonly _operations: IOperations<N>
private readonly _options: ITraverseOptions<N>
private readonly _state: State<N>
private _nextNode: N | undefined
/**
* @param root 树的根节点
*
* @param operations 树、节点的操作
*
* @param options 遍历选项
* @param options.reverse 是否反向遍历. 默认为 false.
* @param options.filter 过滤器. 默认为始终返回 true 的函数.
*/
constructor(root: N, operations: IOperations<N>, options?: Partial<ITraverseOptions<N>>) {
const defaultOptions: ITraverseOptions<N> = {
reverse: false,
filter: () => true
}
const mergedOptions = { ...defaultOptions, ...options }
this._operations = operations
this._options = mergedOptions
this._state = new State()
this._nextNode = root
this._state.push(this._createContext(root))
if (!this._matchesFilter()) this._moveUntilMatch()
}
hasNext(): boolean {
return !!this._nextNode
}
next(): N | undefined {
const res = this._nextNode
this._moveUntilMatch()
return res
}
private _moveUntilMatch(): void {
for (;;) {
if (!this._nextNode) return
this._move()
if (this._matchesFilter()) return
}
}
private _move(): void {
for (;;) {
const context = this._state.current()
if (!context) {
this._nextNode = undefined
return
}
const nextNode = context.next()
if (nextNode) {
this._nextNode = nextNode
this._state.push(this._createContext(nextNode))
return
}
this._state.pop()
}
}
private _matchesFilter(): boolean {
if (!this._nextNode) return false
return this._options.filter(this._nextNode)
}
private _createContext(node: N): Context<N> {
const nextChildFunc = this._operations.getNextChildFunc(node, this._options.reverse)
return new Context(nextChildFunc)
}
}
export type { ITreeIterator }
export { TreeIterator }
if (require.main === module) {
class Node {
// eslint-disable-next-line no-useless-constructor
constructor(
public value: number,
public children: Node[] = []
) {}
}
class Operations implements IOperations<Node> {
getNextChildFunc(node: Node, reverse: boolean): NextChildFunc<Node> {
let index = reverse ? node.children.length : -1
return () => {
index += reverse ? -1 : 1
if (index < 0 || index >= node.children.length) return undefined
return node.children[index]
}
}
}
// 1
// / \
// 2 5
// / \
// 3 4
const root = new Node(1, [new Node(2, [new Node(3), new Node(4)]), new Node(5)])
const isLeaf = (node: Node) => !node.children.length
const iter = new TreeIterator(root, new Operations(), { reverse: true, filter: isLeaf })
while (iter.hasNext()) {
const node = iter.next()!
console.log(node.value)
}
}