Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions data_structures/trees/splay_tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# the_algorithms/trees/splay_tree.py

Check failure on line 1 in data_structures/trees/splay_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (INP001)

data_structures/trees/splay_tree.py:1:1: INP001 File `data_structures/trees/splay_tree.py` is part of an implicit namespace package. Add an `__init__.py`.

Check failure on line 1 in data_structures/trees/splay_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (INP001)

data_structures/trees/splay_tree.py:1:1: INP001 File `data_structures/trees/splay_tree.py` is part of an implicit namespace package. Add an `__init__.py`.

class Node:
"""A single node in the Splay Tree."""
def __init__(self, key, parent=None, left=None, right=None):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: __init__. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: key

Please provide type hint for the parameter: parent

Please provide type hint for the parameter: left

Please provide type hint for the parameter: right

self.key = key
self.parent = parent
self.left = left
self.right = right

class SplayTree:
"""
A self-adjusting Binary Search Tree (BST) that uses the splay operation
to move the most recently accessed node to the root of the tree.
"""
def __init__(self):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: __init__. If the function does not return a value, please provide the type hint as: def function() -> None:

self.root = None

def _rotate(self, x: Node):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: _rotate. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide descriptive name for the parameter: x

"""Performs a single rotation (left or right) around node x."""
p = x.parent # Parent of x
g = p.parent # Grandparent of x

if p.left == x: # Right rotation (x is left child)
p.left = x.right
if x.right:
x.right.parent = p
x.right = p
else: # Left rotation (x is right child)
p.right = x.left
if x.left:
x.left.parent = p
x.left = p

# Update parent pointers
p.parent = x
x.parent = g

# Update grandparent pointer to x
if g:
if g.left == p:
g.left = x
else:
g.right = x
else:
self.root = x # x is the new root

def _splay(self, x: Node):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: _splay. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide descriptive name for the parameter: x

"""Moves node x to the root of the tree using zig, zig-zig, or zig-zag operations."""

Check failure on line 49 in data_structures/trees/splay_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

data_structures/trees/splay_tree.py:49:89: E501 Line too long (93 > 88)

Check failure on line 49 in data_structures/trees/splay_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

data_structures/trees/splay_tree.py:49:89: E501 Line too long (93 > 88)
while x.parent:
p = x.parent
g = p.parent

if not g:
# Zig operation (p is the root)
self._rotate(x)
elif (p.left == x and g.left == p) or (p.right == x and g.right == p):
# Zig-zig operation (x, p, g are all on the left or all on the right)
self._rotate(p) # Rotate p first
self._rotate(x) # Then rotate x
else:
# Zig-zag operation (x is left/right and p is right/left)
self._rotate(x) # Rotate x first
self._rotate(x) # Then rotate x again

def search(self, key):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: search. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: key

"""
Searches for a node with the given key. If found, the node is splayed to the root.

Check failure on line 68 in data_structures/trees/splay_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

data_structures/trees/splay_tree.py:68:89: E501 Line too long (90 > 88)

Check failure on line 68 in data_structures/trees/splay_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

data_structures/trees/splay_tree.py:68:89: E501 Line too long (90 > 88)
If not found, the last accessed node (parent of where the key would be) is splayed.

Check failure on line 69 in data_structures/trees/splay_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

data_structures/trees/splay_tree.py:69:89: E501 Line too long (91 > 88)

Check failure on line 69 in data_structures/trees/splay_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

data_structures/trees/splay_tree.py:69:89: E501 Line too long (91 > 88)
Returns the node if found, otherwise None.
"""
curr = self.root
last = None # Keeps track of the last node accessed

while curr:
last = curr
if key == curr.key:
self._splay(curr)
return curr
elif key < curr.key:
curr = curr.left
else:
curr = curr.right

if last:
self._splay(last) # Splay the last accessed node if key was not found
return None

def insert(self, key):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: insert. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: key

"""Inserts a new key and then splays it to the root."""
if not self.root:
self.root = Node(key)
return

# Regular BST insertion
curr = self.root
parent = None
while curr:
parent = curr
if key < curr.key:
curr = curr.left
elif key > curr.key:
curr = curr.right
else: # Key already exists, splay it and return (or update value)
self._splay(curr)
return

new_node = Node(key, parent=parent)
if key < parent.key:
parent.left = new_node
else:
parent.right = new_node

self._splay(new_node)

Check failure on line 114 in data_structures/trees/splay_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W291)

data_structures/trees/splay_tree.py:114:30: W291 Trailing whitespace

Check failure on line 114 in data_structures/trees/splay_tree.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W291)

data_structures/trees/splay_tree.py:114:30: W291 Trailing whitespace
43 changes: 43 additions & 0 deletions data_structures/trees/splay_tree_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# the_algorithms/trees/splay_tree_test.py

Check failure on line 1 in data_structures/trees/splay_tree_test.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (INP001)

data_structures/trees/splay_tree_test.py:1:1: INP001 File `data_structures/trees/splay_tree_test.py` is part of an implicit namespace package. Add an `__init__.py`.

Check failure on line 1 in data_structures/trees/splay_tree_test.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (INP001)

data_structures/trees/splay_tree_test.py:1:1: INP001 File `data_structures/trees/splay_tree_test.py` is part of an implicit namespace package. Add an `__init__.py`.

import unittest
from splay_tree import SplayTree

Check failure on line 4 in data_structures/trees/splay_tree_test.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

data_structures/trees/splay_tree_test.py:3:1: I001 Import block is un-sorted or un-formatted

Check failure on line 4 in data_structures/trees/splay_tree_test.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

data_structures/trees/splay_tree_test.py:3:1: I001 Import block is un-sorted or un-formatted

class TestSplayTree(unittest.TestCase):
def test_insert_and_root(self):
"""Test basic insertion and verify the splayed node becomes the root."""
tree = SplayTree()
keys = [50, 30, 70, 20, 40]
for key in keys:
tree.insert(key)
self.assertEqual(tree.root.key, key, f"Expected {key} to be the root after insertion.")

Check failure on line 13 in data_structures/trees/splay_tree_test.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

data_structures/trees/splay_tree_test.py:13:89: E501 Line too long (99 > 88)

Check failure on line 13 in data_structures/trees/splay_tree_test.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (PT009)

data_structures/trees/splay_tree_test.py:13:13: PT009 Use a regular `assert` instead of unittest-style `assertEqual`

Check failure on line 13 in data_structures/trees/splay_tree_test.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

data_structures/trees/splay_tree_test.py:13:89: E501 Line too long (99 > 88)

Check failure on line 13 in data_structures/trees/splay_tree_test.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (PT009)

data_structures/trees/splay_tree_test.py:13:13: PT009 Use a regular `assert` instead of unittest-style `assertEqual`

def test_search_and_splay(self):
"""Test searching for an existing key and verify it is splayed to the root."""
tree = SplayTree()
keys = [50, 30, 70, 20, 40, 60, 80]
for key in keys:
tree.insert(key)

# Search for 20. It should become the new root.
found_node = tree.search(20)
self.assertIsNotNone(found_node)

Check failure on line 24 in data_structures/trees/splay_tree_test.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (PT009)

data_structures/trees/splay_tree_test.py:24:9: PT009 Use a regular `assert` instead of unittest-style `assertIsNotNone`

Check failure on line 24 in data_structures/trees/splay_tree_test.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (PT009)

data_structures/trees/splay_tree_test.py:24:9: PT009 Use a regular `assert` instead of unittest-style `assertIsNotNone`
self.assertEqual(found_node.key, 20)
self.assertEqual(tree.root.key, 20, "20 should be the root after search.")

# Search for a key that doesn't exist (99). The last accessed node (e.g., 80) should be splayed.
_ = tree.search(99)
# The exact last accessed node depends on the tree structure, but it should not be the original root (50)
self.assertNotEqual(tree.root.key, 50, "Root should change after unsuccessful search.")

def test_empty_tree(self):
"""Test operations on an empty tree."""
tree = SplayTree()
self.assertIsNone(tree.search(10))
self.assertIsNone(tree.root)

tree.insert(10)
self.assertEqual(tree.root.key, 10)

if __name__ == '__main__':
unittest.main()