Skip to content

Commit 50c2b34

Browse files
Add Splay Tree implementation (#13760) [hacktoberfest]
This PR adds a Splay Tree (Self Adjusting BST) implementation in Python under the `data_structures/binary_tree/` directory. ## Details: - Implements the Splay Tree data structure with all core operations: insertion, search, splay rotation, and inorder traversal. - Includes class-based design (`Node`, `SplayTree`) and follows Python community style. - Provides an example usage and test in the `__main__` block. ## Why Splay Tree? A Splay Tree is a self-adjusting binary search tree that provides fast access to recently used elements by moving them closer to the root through 'splaying' operations. Frequently accessed nodes move towards the top, which is useful for caching and memory management systems. ## Issue Reference This solves [Issue #13760](#13760). ## Hacktoberfest Marking this contribution for Hacktoberfest 2025. **For Maintainers:** Please add the `hacktoberfest-accepted` label to count this PR towards Hacktoberfest. ## Checklist - [x] Follows contribution guidelines - [x] Code is documented with docstrings and inline comments - [x] Includes an example/test case - [x] New file in correct directory --- Feel free to use or modify the text above for your commit and pull request! If you want it in Hinglish or need a quick summary, let me know.
1 parent 22f137d commit 50c2b34

File tree

1 file changed

+42
-24
lines changed

1 file changed

+42
-24
lines changed

data_structures/binary_tree/splay_tree.py

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,18 @@ def __init__(self, key):
1212
self.parent = None
1313
self.key = key
1414

15+
def __repr__(self):
16+
return f"Node({self.key})"
17+
1518

1619
class SplayTree:
1720
def __init__(self):
1821
self.root = None
1922

2023
def _right_rotate(self, x):
2124
y = x.left
25+
if not y:
26+
return
2227
x.left = y.right
2328
if y.right:
2429
y.right.parent = x
@@ -34,6 +39,8 @@ def _right_rotate(self, x):
3439

3540
def _left_rotate(self, x):
3641
y = x.right
42+
if not y:
43+
return
3744
x.right = y.left
3845
if y.left:
3946
y.left.parent = x
@@ -48,25 +55,30 @@ def _left_rotate(self, x):
4855
x.parent = y
4956

5057
def _splay(self, x):
51-
while x.parent:
52-
if not x.parent.parent:
53-
if x.parent.left == x:
54-
self._right_rotate(x.parent)
58+
while x and x.parent:
59+
p = x.parent
60+
g = p.parent
61+
# Zig step (p is root)
62+
if not g:
63+
if p.left == x:
64+
self._right_rotate(p)
5565
else:
56-
self._left_rotate(x.parent)
66+
self._left_rotate(p)
5767
else:
58-
if x.parent.left == x and x.parent.parent.left == x.parent:
59-
self._right_rotate(x.parent.parent)
60-
self._right_rotate(x.parent)
61-
elif x.parent.right == x and x.parent.parent.right == x.parent:
62-
self._left_rotate(x.parent.parent)
63-
self._left_rotate(x.parent)
64-
elif x.parent.left == x and x.parent.parent.right == x.parent:
65-
self._right_rotate(x.parent)
66-
self._left_rotate(x.parent)
67-
else:
68-
self._left_rotate(x.parent)
69-
self._right_rotate(x.parent)
68+
# Zig-Zig
69+
if p.left == x and g.left == p:
70+
self._right_rotate(g)
71+
self._right_rotate(p)
72+
elif p.right == x and g.right == p:
73+
self._left_rotate(g)
74+
self._left_rotate(p)
75+
# Zig-Zag
76+
elif p.left == x and g.right == p:
77+
self._right_rotate(p)
78+
self._left_rotate(g)
79+
else: # p.right == x and g.left == p
80+
self._left_rotate(p)
81+
self._right_rotate(g)
7082

7183
def insert(self, key):
7284
z = self.root
@@ -89,26 +101,29 @@ def insert(self, key):
89101

90102
def search(self, key):
91103
z = self.root
104+
last = None
92105
while z:
106+
last = z
93107
if key == z.key:
94108
self._splay(z)
95109
return z
96110
elif key < z.key:
97-
if not z.left:
98-
self._splay(z)
99-
return None
100111
z = z.left
101112
else:
102-
if not z.right:
103-
self._splay(z)
104-
return None
105113
z = z.right
114+
# splay the last accessed node (closest) if present
115+
if last:
116+
self._splay(last)
106117
return None
107118

108119
def inorder(self, node=None, result=None):
109120
if result is None:
110121
result = []
111-
node = node or self.root
122+
# if node is explicitly passed as None and tree is empty, return empty result
123+
if node is None:
124+
node = self.root
125+
if node is None:
126+
return result
112127
if node.left:
113128
self.inorder(node.left, result)
114129
result.append(node.key)
@@ -120,6 +135,9 @@ def inorder(self, node=None, result=None):
120135
# Example Usage / Test
121136
if __name__ == "__main__":
122137
tree = SplayTree()
138+
# empty tree -> inorder should return []
139+
print(tree.inorder()) # []
140+
123141
for key in [10, 20, 30, 40, 50, 25]:
124142
tree.insert(key)
125143
print(tree.inorder()) # Output should be the inorder traversal of tree

0 commit comments

Comments
 (0)