20250224 ‐ splay tree - cywongg/2025 GitHub Wiki
Let’s clarify the difference in the removal logic for a Splay Tree based on the description you shared. Normally (in many standard splay tree implementations), the removal procedure is something like:
- Find the node (which splays it to the root if found).
- Remove the root.
- Reconnect left and right subtrees (e.g., by splaying the max of the left subtree and attaching the right subtree).
However, the description you’ve given uses regular BST deletion first (e.g., swapping with predecessor/successor, etc.) and then it specifically says:
“The node to be splayed is the original node’s parent… If the element is not present, we splay the last element accessed.”
This is a different approach than the conventional “splay the node to remove itself first.” So, yes, you would change the code I shared earlier if you need to follow that definition.
Usual (“Conventional”) vs. Your (“Different”) Removal Approach
-
Conventional:
- Splay the target node to root.
- Remove root.
- Reattach children.
-
Your described approach:
- Do a BST-style delete:
- If node has 0 or 1 child → remove directly.
- If node has 2 children → swap with predecessor/successor, then remove.
- After actually removing that node, splay the (former) parent of the deleted node (or splay the last accessed node if the item wasn’t found).
- Do a BST-style delete:
In other words, you don’t splay the node itself to root before deletion. Instead, you delete it the “old-fashioned” BST way, then you splay its parent.
Outline: How to Modify the Code
Let’s see how you might modify the previous code snippet to follow this new logic. The core difference is inside the remove
method. Here’s a sample approach:
- Locate the node (but do not splay it automatically). You can do a standard “search” for it—like a regular BST search that does not do any splaying.
- If the node is not found:
- Splay the last accessed node (like a normal splay-tree “not found” scenario).
- If the node is found:
- Remember the node’s parent (call it
p
) or the last accessed node. - Perform the standard BST removal procedure (swap with predecessor or successor if 2 children, or remove directly if 0 or 1 child).
- If
p
still exists (i.e., the removed node wasn’t root or something), splayp
.
- Remember the node’s parent (call it
Below is a simplified snippet just focusing on how you might do it. For brevity, I’ll pseudo-code some parts:
// We won't call `findNode(val)` which does an automatic splay in its search.
// Instead, we do a separate "searchNoSplay" that only returns the node or null.
// If not found, we splay the last accessed node.
public void remove(E val) {
if (val == null) return;
// We'll do a "BST-like" search that doesn't splay automatically
SearchResult sr = searchNoSplay(val);
Node target = sr.foundNode; // the node with 'val' if found
Node lastAccessed = sr.lastNode; // the last node visited
if (target == null) {
// not found, so per the spec: splay the last accessed node
if (lastAccessed != null) {
splay(lastAccessed);
}
return;
}
// If found, store its parent for splaying after we physically delete
Node p = target.parent;
// Standard BST removal logic:
// 1) If target has 0 or 1 child, remove directly.
// 2) If target has 2 children, swap with predecessor (or successor),
// then treat it as 0 or 1 child case.
if (target.left == null || target.right == null) {
// easy 0 or 1 child remove
removeNodeDirect(target);
} else {
// swap with predecessor
Node pred = findMax(target.left);
// swap the values (not pointers!!) so target is now pred, effectively
E temp = target.value;
target.value = pred.value;
pred.value = temp;
// Now pred has at most 1 child
removeNodeDirect(pred);
// p might change if pred had a different parent
// but let's keep it simple and let p remain as is
}
// Finally, if p != null, we splay p
if (p != null) {
splay(p);
}
}
searchNoSplay
Helper
The We need a search that returns both:
- The node if found.
- The last node visited if not found (or if found).
Something like:
private class SearchResult {
Node foundNode;
Node lastNode;
}
private SearchResult searchNoSplay(E val) {
SearchResult sr = new SearchResult();
sr.foundNode = null;
sr.lastNode = null;
Node current = root;
Node parent = null;
while (current != null) {
parent = current;
int cmp = val.compareTo(current.value);
if (cmp == 0) {
sr.foundNode = current; // found it
sr.lastNode = current;
return sr;
} else if (cmp < 0) {
current = current.left;
} else {
current = current.right;
}
}
// if we get here, we never found it
sr.foundNode = null;
sr.lastNode = parent; // the last node we visited
return sr;
}
removeNodeDirect
Helper
The This removes a node with at most one child in normal BST fashion, adjusting parents:
private void removeNodeDirect(Node node) {
Node child = (node.left != null) ? node.left : node.right;
// Link child's parent to node's parent
if (child != null) {
child.parent = node.parent;
}
// If node is root
if (node.parent == null) {
root = child;
} else if (node == node.parent.left) {
node.parent.left = child;
} else {
node.parent.right = child;
}
// 'node' is effectively removed now
}
Summarizing the Differences
- In most splay tree textbooks: we do “splay the node we want to remove” to the root, then remove root.
- Your described approach: do “normal BST removal” (including predecessor/successor swap if 2 children) and then splay the parent of the removed node.
Both are valid ways of implementing a splay tree. They each have slightly different performance characteristics but still maintain the amortized efficiency splay trees are known for.
TL;DR
- Yes, using that definition changes the code from the conventional approach.
- Instead of splaying the node being removed first, you do a standard BST delete, then splay the parent afterward.
- If the node isn’t found, you splay the last node visited just like normal splay trees do.
So to answer your question directly: Yes—if you need the removal method to behave exactly as described (swap with predecessor and then splay the parent of the original node), you do have to modify the removal logic from the conventional “splay the node to root, then remove.”