203 lines
8.0 KiB
Plaintext
203 lines
8.0 KiB
Plaintext
DeepSeek Antwort
|
|
##################################################################################################################
|
|
|
|
To address the problem of implementing and visualizing a red-black tree, we divide the solution into several steps: creating the `Node` class, implementing the red-black tree (`RedBlackTree`) with insertion and balancing operations, and visualizing the tree using Matplotlib. Each step is explained in detail, followed by the complete code.
|
|
|
|
### Step-by-Step Explanations
|
|
|
|
1. **Node Class**:
|
|
- Represents each node in the tree with attributes for value, color, left/right children, and parent.
|
|
- New nodes are initialized as red, with children pointing to a sentinel `NIL` node.
|
|
|
|
2. **RedBlackTree Class**:
|
|
- Manages the tree structure, including insertion and balancing.
|
|
- Uses a sentinel `NIL` node (black) to represent leaves.
|
|
- Insertion follows BST rules, followed by fixing the tree to maintain red-black properties using rotations and recoloring.
|
|
|
|
3. **Tree Balancing (Insert Fix)**:
|
|
- After inserting a node as red, fix violations of red-black properties.
|
|
- Cases handled based on the uncle's color and node position (left/right child).
|
|
|
|
4. **Rotations**:
|
|
- Left and right rotations adjust the tree structure to balance it after insertion.
|
|
|
|
5. **Visualization**:
|
|
- Assigns coordinates to each node using a modified in-order traversal for horizontal placement and depth for vertical placement.
|
|
- Uses Matplotlib to draw nodes (colored circles) and edges (lines between nodes).
|
|
|
|
### Solution Code
|
|
|
|
```python
|
|
import matplotlib.pyplot as plt
|
|
|
|
class Node:
|
|
def __init__(self, value, color='red'):
|
|
self.value = value
|
|
self.color = color
|
|
self.left = None
|
|
self.right = None
|
|
self.parent = None
|
|
|
|
def __repr__(self):
|
|
return f"Node({self.value}, {self.color})"
|
|
|
|
class RedBlackTree:
|
|
def __init__(self):
|
|
self.NIL = Node(None, 'black')
|
|
self.NIL.left = self.NIL
|
|
self.NIL.right = self.NIL
|
|
self.NIL.parent = self.NIL
|
|
self.root = self.NIL
|
|
|
|
def insert(self, value):
|
|
new_node = Node(value)
|
|
new_node.left = self.NIL
|
|
new_node.right = self.NIL
|
|
new_node.parent = self.NIL
|
|
|
|
current = self.root
|
|
parent = self.NIL
|
|
while current != self.NIL:
|
|
parent = current
|
|
if new_node.value < current.value:
|
|
current = current.left
|
|
else:
|
|
current = current.right
|
|
new_node.parent = parent
|
|
if parent == self.NIL:
|
|
self.root = new_node
|
|
elif new_node.value < parent.value:
|
|
parent.left = new_node
|
|
else:
|
|
parent.right = new_node
|
|
|
|
self.fix_insert(new_node)
|
|
|
|
def fix_insert(self, node):
|
|
while node.parent.color == 'red':
|
|
if node.parent == node.parent.parent.left:
|
|
uncle = node.parent.parent.right
|
|
if uncle.color == 'red':
|
|
node.parent.color = 'black'
|
|
uncle.color = 'black'
|
|
node.parent.parent.color = 'red'
|
|
node = node.parent.parent
|
|
else:
|
|
if node == node.parent.right:
|
|
node = node.parent
|
|
self.left_rotate(node)
|
|
node.parent.color = 'black'
|
|
node.parent.parent.color = 'red'
|
|
self.right_rotate(node.parent.parent)
|
|
else:
|
|
uncle = node.parent.parent.left
|
|
if uncle.color == 'red':
|
|
node.parent.color = 'black'
|
|
uncle.color = 'black'
|
|
node.parent.parent.color = 'red'
|
|
node = node.parent.parent
|
|
else:
|
|
if node == node.parent.left:
|
|
node = node.parent
|
|
self.right_rotate(node)
|
|
node.parent.color = 'black'
|
|
node.parent.parent.color = 'red'
|
|
self.left_rotate(node.parent.parent)
|
|
if node == self.root:
|
|
break
|
|
self.root.color = 'black'
|
|
|
|
def left_rotate(self, x):
|
|
y = x.right
|
|
x.right = y.left
|
|
if y.left != self.NIL:
|
|
y.left.parent = x
|
|
y.parent = x.parent
|
|
if x.parent == self.NIL:
|
|
self.root = y
|
|
elif x == x.parent.left:
|
|
x.parent.left = y
|
|
else:
|
|
x.parent.right = y
|
|
y.left = x
|
|
x.parent = y
|
|
|
|
def right_rotate(self, y):
|
|
x = y.left
|
|
y.left = x.right
|
|
if x.right != self.NIL:
|
|
x.right.parent = y
|
|
x.parent = y.parent
|
|
if y.parent == self.NIL:
|
|
self.root = x
|
|
elif y == y.parent.right:
|
|
y.parent.right = x
|
|
else:
|
|
y.parent.left = x
|
|
x.right = y
|
|
y.parent = x
|
|
|
|
def _assign_positions(self, node, pos_map, x=0, depth=0):
|
|
if node == self.NIL:
|
|
return x
|
|
x = self._assign_positions(node.left, pos_map, x, depth + 1)
|
|
pos_map[node] = (x, depth)
|
|
x += 1
|
|
x = self._assign_positions(node.right, pos_map, x, depth + 1)
|
|
return x
|
|
|
|
def visualize(self):
|
|
pos_map = {}
|
|
self._assign_positions(self.root, pos_map)
|
|
if not pos_map:
|
|
print("Tree is empty.")
|
|
return
|
|
|
|
fig, ax = plt.subplots()
|
|
max_x = max(x for x, _ in pos_map.values())
|
|
max_depth = max(d for _, d in pos_map.values())
|
|
|
|
h_spacing = 1.0
|
|
v_spacing = 1.0
|
|
|
|
for node in pos_map:
|
|
x, depth = pos_map[node]
|
|
scaled_x = x * h_spacing
|
|
scaled_y = -depth * v_spacing
|
|
|
|
if node.left != self.NIL:
|
|
l_x, l_depth = pos_map[node.left]
|
|
ax.plot([scaled_x, l_x * h_spacing], [scaled_y, -l_depth * v_spacing], 'k-', linewidth=1)
|
|
if node.right != self.NIL:
|
|
r_x, r_depth = pos_map[node.right]
|
|
ax.plot([scaled_x, r_x * h_spacing], [scaled_y, -r_depth * v_spacing], 'k-', linewidth=1)
|
|
|
|
color = 'red' if node.color == 'red' else 'black'
|
|
circle = plt.Circle((scaled_x, scaled_y), 0.3, color=color, ec='k')
|
|
ax.add_patch(circle)
|
|
text_color = 'white' if node.color == 'black' else 'black'
|
|
plt.text(scaled_x, scaled_y, str(node.value), color=text_color, ha='center', va='center', fontsize=10)
|
|
|
|
ax.set_xlim(-h_spacing, (max_x + 1) * h_spacing)
|
|
ax.set_ylim(-(max_depth + 1) * v_spacing, v_spacing)
|
|
ax.set_aspect('equal')
|
|
ax.axis('off')
|
|
plt.show()
|
|
```
|
|
|
|
### Explanation and Assumptions
|
|
|
|
- **Node Class**: Stores value, color, and references to left/right children and parent. The default color for new nodes is red.
|
|
- **RedBlackTree Class**: Manages tree operations. Uses a sentinel `NIL` node to represent leaves (all black).
|
|
- **Insertion and Balancing**: After BST insertion, the tree is rebalanced using rotations and recoloring based on the uncle node's color and position.
|
|
- **Visualization**: Nodes are positioned using in-order traversal to determine horizontal placement and depth for vertical placement. Nodes are drawn as circles with their values, colored according to red-black properties.
|
|
|
|
### Limitations and Ambiguities
|
|
|
|
- **Visualization**: The in-order traversal-based positioning might not perfectly center parent nodes over their children, especially in unbalanced trees. This can lead to overlapping or misaligned nodes in complex structures.
|
|
- **Functionality**: The implementation does not handle node deletion, which is more complex and requires additional balancing logic.
|
|
- **Edge Cases**: The code assumes unique values; inserting duplicates may lead to incorrect behavior.
|
|
- **Performance**: The visualization may not scale efficiently for very large trees due to the simple positioning algorithm.
|
|
|
|
This solution provides a foundational implementation of a red-black tree with visualization, suitable for educational purposes and moderate-sized trees.
|