Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

node.gno

12.48 Kb · 488 lines
  1package avl
  2
  3// Node represents a node in an AVL tree.
  4type Node struct {
  5	key       string // key is the unique identifier for the node.
  6	value     any    // value is the data stored in the node.
  7	height    int8   // height is the height of the node in the tree.
  8	size      int    // size is the number of leaf nodes (key-value pairs) in the subtree rooted at this node.
  9	leftNode  *Node  // leftNode is the left child of the node.
 10	rightNode *Node  // rightNode is the right child of the node.
 11}
 12
 13// NewNode creates a new node with the given key and value.
 14func NewNode(key string, value any) *Node {
 15	return &Node{
 16		key:    key,
 17		value:  value,
 18		height: 0,
 19		size:   1,
 20	}
 21}
 22
 23// Size returns the size of the subtree rooted at the node.
 24func (node *Node) Size() int {
 25	if node == nil {
 26		return 0
 27	}
 28	return node.size
 29}
 30
 31// IsLeaf checks if the node is a leaf node (has no children).
 32func (node *Node) IsLeaf() bool {
 33	return node.height == 0
 34}
 35
 36// Key returns the key of the node.
 37func (node *Node) Key() string {
 38	return node.key
 39}
 40
 41// Value returns the value of the node.
 42func (node *Node) Value() any {
 43	return node.value
 44}
 45
 46func (node *Node) _copy() *Node {
 47	if node.height == 0 {
 48		panic("Why are you copying a value node?")
 49	}
 50	return &Node{
 51		key:       node.key,
 52		height:    node.height,
 53		size:      node.size,
 54		leftNode:  node.leftNode,
 55		rightNode: node.rightNode,
 56	}
 57}
 58
 59// Has checks if a node with the given key exists in the subtree rooted at the node.
 60func (node *Node) Has(key string) (has bool) {
 61	if node == nil {
 62		return false
 63	}
 64	if node.key == key {
 65		return true
 66	}
 67	if node.height == 0 {
 68		return false
 69	} else {
 70		if key < node.key {
 71			return node.getLeftNode().Has(key)
 72		} else {
 73			return node.getRightNode().Has(key)
 74		}
 75	}
 76}
 77
 78// Get searches for a node with the given key in the subtree rooted at the node
 79// and returns its index, value, and whether it exists.
 80func (node *Node) Get(key string) (index int, value any, exists bool) {
 81	if node == nil {
 82		return 0, nil, false
 83	}
 84
 85	if node.height == 0 {
 86		if node.key == key {
 87			return 0, node.value, true
 88		} else if node.key < key {
 89			return 1, nil, false
 90		} else {
 91			return 0, nil, false
 92		}
 93	} else {
 94		if key < node.key {
 95			return node.getLeftNode().Get(key)
 96		} else {
 97			rightNode := node.getRightNode()
 98			index, value, exists = rightNode.Get(key)
 99			index += node.size - rightNode.size
100			return index, value, exists
101		}
102	}
103}
104
105// GetByIndex retrieves the key-value pair of the node at the given index
106// in the subtree rooted at the node.
107func (node *Node) GetByIndex(index int) (key string, value any) {
108	if index < 0 {
109		panic("GetByIndex: negative index not allowed")
110	}
111
112	if node.height == 0 {
113		if index != 0 {
114			panic("GetByIndex asked for invalid index")
115		}
116		return node.key, node.value
117	} else {
118		// TODO: could improve this by storing the sizes
119		leftNode := node.getLeftNode()
120		if index < leftNode.size {
121			return leftNode.GetByIndex(index)
122		} else {
123			return node.getRightNode().GetByIndex(index - leftNode.size)
124		}
125	}
126}
127
128// Set inserts a new node with the given key-value pair into the subtree rooted at the node,
129// and returns the new root of the subtree and whether an existing node was updated.
130//
131// XXX consider a better way to do this... perhaps split Node from Node.
132func (node *Node) Set(key string, value any) (newSelf *Node, updated bool) {
133	if node == nil {
134		return NewNode(key, value), false
135	}
136	if node.height == 0 {
137		if key < node.key {
138			return &Node{
139				key:       node.key,
140				height:    1,
141				size:      2,
142				leftNode:  NewNode(key, value),
143				rightNode: node,
144			}, false
145		} else if key == node.key {
146			return NewNode(key, value), true
147		} else {
148			return &Node{
149				key:       key,
150				height:    1,
151				size:      2,
152				leftNode:  node,
153				rightNode: NewNode(key, value),
154			}, false
155		}
156	} else {
157		node = node._copy()
158		if key < node.key {
159			node.leftNode, updated = node.getLeftNode().Set(key, value)
160		} else {
161			node.rightNode, updated = node.getRightNode().Set(key, value)
162		}
163		if updated {
164			return node, updated
165		} else {
166			node.calcHeightAndSize()
167			return node.balance(), updated
168		}
169	}
170}
171
172// Remove deletes the node with the given key from the subtree rooted at the node.
173// returns the new root of the subtree, the new leftmost leaf key (if changed),
174// the removed value and the removal was successful.
175func (node *Node) Remove(key string) (
176	newNode *Node, newKey string, value any, removed bool,
177) {
178	if node == nil {
179		return nil, "", nil, false
180	}
181	if node.height == 0 {
182		if key == node.key {
183			return nil, "", node.value, true
184		} else {
185			return node, "", nil, false
186		}
187	} else {
188		if key < node.key {
189			var newLeftNode *Node
190			newLeftNode, newKey, value, removed = node.getLeftNode().Remove(key)
191			if !removed {
192				return node, "", value, false
193			} else if newLeftNode == nil { // left node held value, was removed
194				return node.rightNode, node.key, value, true
195			}
196			node = node._copy()
197			node.leftNode = newLeftNode
198			node.calcHeightAndSize()
199			node = node.balance()
200			return node, newKey, value, true
201		} else {
202			var newRightNode *Node
203			newRightNode, newKey, value, removed = node.getRightNode().Remove(key)
204			if !removed {
205				return node, "", value, false
206			} else if newRightNode == nil { // right node held value, was removed
207				return node.leftNode, "", value, true
208			}
209			node = node._copy()
210			node.rightNode = newRightNode
211			if newKey != "" {
212				node.key = newKey
213			}
214			node.calcHeightAndSize()
215			node = node.balance()
216			return node, "", value, true
217		}
218	}
219}
220
221func (node *Node) getLeftNode() *Node {
222	return node.leftNode
223}
224
225func (node *Node) getRightNode() *Node {
226	return node.rightNode
227}
228
229// rotateRight performs a right rotation on the node and returns the new root.
230// NOTE: overwrites node
231// TODO: optimize balance & rotate
232func (node *Node) rotateRight() *Node {
233	node = node._copy()
234	l := node.getLeftNode()
235	_l := l._copy()
236
237	_lrCached := _l.rightNode
238	_l.rightNode = node
239	node.leftNode = _lrCached
240
241	node.calcHeightAndSize()
242	_l.calcHeightAndSize()
243
244	return _l
245}
246
247// rotateLeft performs a left rotation on the node and returns the new root.
248// NOTE: overwrites node
249// TODO: optimize balance & rotate
250func (node *Node) rotateLeft() *Node {
251	node = node._copy()
252	r := node.getRightNode()
253	_r := r._copy()
254
255	_rlCached := _r.leftNode
256	_r.leftNode = node
257	node.rightNode = _rlCached
258
259	node.calcHeightAndSize()
260	_r.calcHeightAndSize()
261
262	return _r
263}
264
265// calcHeightAndSize updates the height and size of the node based on its children.
266// NOTE: mutates height and size
267func (node *Node) calcHeightAndSize() {
268	node.height = maxInt8(node.getLeftNode().height, node.getRightNode().height) + 1
269	node.size = node.getLeftNode().size + node.getRightNode().size
270}
271
272// calcBalance calculates the balance factor of the node.
273func (node *Node) calcBalance() int {
274	return int(node.getLeftNode().height) - int(node.getRightNode().height)
275}
276
277// balance balances the subtree rooted at the node and returns the new root.
278// NOTE: assumes that node can be modified
279// TODO: optimize balance & rotate
280func (node *Node) balance() (newSelf *Node) {
281	balance := node.calcBalance()
282	if balance > 1 {
283		if node.getLeftNode().calcBalance() >= 0 {
284			// Left Left Case
285			return node.rotateRight()
286		} else {
287			// Left Right Case
288			left := node.getLeftNode()
289			node.leftNode = left.rotateLeft()
290			return node.rotateRight()
291		}
292	}
293	if balance < -1 {
294		if node.getRightNode().calcBalance() <= 0 {
295			// Right Right Case
296			return node.rotateLeft()
297		} else {
298			// Right Left Case
299			right := node.getRightNode()
300			node.rightNode = right.rotateRight()
301			return node.rotateLeft()
302		}
303	}
304	// Nothing changed
305	return node
306}
307
308// Shortcut for TraverseInRange.
309func (node *Node) Iterate(start, end string, cb func(*Node) bool) bool {
310	return node.TraverseInRange(start, end, true, true, cb)
311}
312
313// Shortcut for TraverseInRange.
314func (node *Node) ReverseIterate(start, end string, cb func(*Node) bool) bool {
315	return node.TraverseInRange(start, end, false, true, cb)
316}
317
318// TraverseInRange traverses all nodes, including inner nodes.
319// Start is inclusive and end is exclusive when ascending,
320// Start and end are inclusive when descending.
321// Empty start and empty end denote no start and no end.
322// If leavesOnly is true, only visit leaf nodes.
323// NOTE: To simulate an exclusive reverse traversal,
324// just append 0x00 to start.
325func (node *Node) TraverseInRange(start, end string, ascending bool, leavesOnly bool, cb func(*Node) bool) bool {
326	if node == nil {
327		return false
328	}
329	afterStart := (start == "" || start < node.key)
330	startOrAfter := (start == "" || start <= node.key)
331	beforeEnd := false
332	if ascending {
333		beforeEnd = (end == "" || node.key < end)
334	} else {
335		beforeEnd = (end == "" || node.key <= end)
336	}
337
338	// Run callback per inner/leaf node.
339	stop := false
340	if (!node.IsLeaf() && !leavesOnly) ||
341		(node.IsLeaf() && startOrAfter && beforeEnd) {
342		stop = cb(node)
343		if stop {
344			return stop
345		}
346	}
347	if node.IsLeaf() {
348		return stop
349	}
350
351	if ascending {
352		// check lower nodes, then higher
353		if afterStart {
354			stop = node.getLeftNode().TraverseInRange(start, end, ascending, leavesOnly, cb)
355		}
356		if stop {
357			return stop
358		}
359		if beforeEnd {
360			stop = node.getRightNode().TraverseInRange(start, end, ascending, leavesOnly, cb)
361		}
362	} else {
363		// check the higher nodes first
364		if beforeEnd {
365			stop = node.getRightNode().TraverseInRange(start, end, ascending, leavesOnly, cb)
366		}
367		if stop {
368			return stop
369		}
370		if afterStart {
371			stop = node.getLeftNode().TraverseInRange(start, end, ascending, leavesOnly, cb)
372		}
373	}
374
375	return stop
376}
377
378// TraverseByOffset traverses all nodes, including inner nodes.
379// A limit of math.MaxInt means no limit.
380func (node *Node) TraverseByOffset(offset, limit int, ascending bool, leavesOnly bool, cb func(*Node) bool) bool {
381	if node == nil {
382		return false
383	}
384
385	// Clamp negative offset to 0; otherwise `delta := first.size - offset`
386	// over-counts and silently drops nodes.
387	if offset < 0 {
388		offset = 0
389	}
390
391	// fast paths. these happen only if TraverseByOffset is called directly on a leaf.
392	if limit <= 0 || offset >= node.size {
393		return false
394	}
395	if node.IsLeaf() {
396		if offset > 0 {
397			return false
398		}
399		return cb(node)
400	}
401
402	// go to the actual recursive function.
403	return node.traverseByOffset(offset, limit, ascending, leavesOnly, cb)
404}
405
406// TraverseByOffset traverses the subtree rooted at the node by offset and limit,
407// in either ascending or descending order, and applies the callback function to each traversed node.
408// If leavesOnly is true, only leaf nodes are visited.
409func (node *Node) traverseByOffset(offset, limit int, ascending bool, leavesOnly bool, cb func(*Node) bool) bool {
410	// caller guarantees: offset < node.size; limit > 0.
411	if !leavesOnly {
412		if cb(node) {
413			return true // Stop traversal if callback returns true
414		}
415	}
416	first, second := node.getLeftNode(), node.getRightNode()
417	if !ascending {
418		first, second = second, first
419	}
420	if first.IsLeaf() {
421		// either run or skip, based on offset
422		if offset > 0 {
423			offset--
424		} else {
425			if cb(first) {
426				return true // Stop traversal if callback returns true
427			}
428			limit--
429			if limit <= 0 {
430				return true // Stop traversal when limit is reached
431			}
432		}
433	} else {
434		// possible cases:
435		// 1 the offset given skips the first node entirely
436		// 2 the offset skips none or part of the first node, but the limit requires some of the second node.
437		// 3 the offset skips none or part of the first node, and the limit stops our search on the first node.
438		if offset >= first.size {
439			offset -= first.size // 1
440		} else {
441			if first.traverseByOffset(offset, limit, ascending, leavesOnly, cb) {
442				return true
443			}
444			// number of leaves which could actually be called from inside
445			delta := first.size - offset
446			offset = 0
447			if delta >= limit {
448				return true // 3
449			}
450			limit -= delta // 2
451		}
452	}
453
454	// because of the caller guarantees and the way we handle the first node,
455	// at this point we know that limit > 0 and there must be some values in
456	// this second node that we include.
457
458	// => if the second node is a leaf, it has to be included.
459	if second.IsLeaf() {
460		return cb(second)
461	}
462	// => if it is not a leaf, it will still be enough to recursively call this
463	// function with the updated offset and limit
464	return second.traverseByOffset(offset, limit, ascending, leavesOnly, cb)
465}
466
467// Only used in testing...
468func (node *Node) lmd() *Node {
469	if node.height == 0 {
470		return node
471	}
472	return node.getLeftNode().lmd()
473}
474
475// Only used in testing...
476func (node *Node) rmd() *Node {
477	if node.height == 0 {
478		return node
479	}
480	return node.getRightNode().rmd()
481}
482
483func maxInt8(a, b int8) int8 {
484	if a > b {
485		return a
486	}
487	return b
488}