Skip to content

Commit 2c4cc0b

Browse files
RedBlackTree fix implementation of remove and insert operation. Added more unit tests.
1 parent f646ffd commit 2c4cc0b

File tree

3 files changed

+530
-158
lines changed

3 files changed

+530
-158
lines changed

DataStructures/Trees/BinarySearchTreeNode.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ public virtual bool HasLeftChild
6161
get { return (this.LeftChild != null); }
6262
}
6363

64+
/// <summary>
65+
/// Check if this node has only one child and whether it is the right child.
66+
/// </summary>
67+
public virtual bool HasOnlyRightChild => !this.HasLeftChild && this.HasRightChild;
68+
6469
/// <summary>
6570
/// Checks whether this node has right child.
6671
/// </summary>
@@ -69,6 +74,11 @@ public virtual bool HasRightChild
6974
get { return (this.RightChild != null); }
7075
}
7176

77+
/// <summary>
78+
/// Check if this node has only one child and whether it is the left child.
79+
/// </summary>
80+
public virtual bool HasOnlyLeftChild => !this.HasRightChild && this.HasLeftChild;
81+
7282
/// <summary>
7383
/// Checks whether this node is the left child of it's parent.
7484
/// </summary>

DataStructures/Trees/RedBlackTree.cs

Lines changed: 64 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
using System;
1+
using DataStructures.Common;
2+
using System;
23
using System.Collections.Generic;
34

4-
using DataStructures.Common;
5-
65
namespace DataStructures.Trees
76
{
87
/// <summary>
@@ -233,8 +232,8 @@ protected virtual void _adjustTreeAfterInsertion(RedBlackTreeNode<TKey> currentN
233232
// This is the simplest step: Basically recolor, and bubble up to see if more work is needed.
234233
if (_safeCheckIsRed(_safeGetSibling(currentNode.Parent)))
235234
{
236-
// If it has a sibling and it is red, then then it has a parent
237-
currentNode.Parent.Color = RedBlackTreeColors.Red;
235+
// If it has a sibling and it is black, then then it has a parent
236+
currentNode.Parent.Color = RedBlackTreeColors.Black;
238237

239238
// Color sibling of parent as black
240239
_safeUpdateColor(_safeGetSibling(currentNode.Parent), RedBlackTreeColors.Black);
@@ -455,80 +454,97 @@ protected override bool _remove(BSTNode<TKey> nodeToDelete)
455454
}
456455

457456
/// <summary>
458-
/// The internal remove helper.
459-
/// Separated from the overriden version to avoid casting the objects from BSTNode to RedBlackTreeNode.
460-
/// This is called from the overriden _remove(BSTNode nodeToDelete) helper.
457+
/// The internal remove helper.
458+
/// Separated from the overriden version to avoid casting the objects from BSTNode to RedBlackTreeNode.
459+
/// This is called from the overriden _remove(BSTNode nodeToDelete) helper.
461460
/// </summary>
462461
protected bool _remove(RedBlackTreeNode<TKey> nodeToDelete)
463462
{
464463
if (nodeToDelete == null)
464+
{
465465
return false;
466+
}
466467

467-
// Temporary nodes
468-
RedBlackTreeNode<TKey> node1, node2;
468+
// Node X moves to the original nodeToDelete position in the tree.
469+
RedBlackTreeNode<TKey> x;
469470

470-
// If nodeToDelete has either one child or no children at all
471-
if (!nodeToDelete.HasLeftChild || !nodeToDelete.HasRightChild)
471+
if (nodeToDelete.HasOnlyRightChild)
472472
{
473-
node1 = nodeToDelete;
473+
x = nodeToDelete.RightChild;
474+
Transplant(nodeToDelete, nodeToDelete.RightChild);
474475
}
475-
else
476+
else if (nodeToDelete.HasOnlyLeftChild)
476477
{
477-
// nodeToDelete has two children
478-
node1 = (RedBlackTreeNode<TKey>)base._findNextLarger(nodeToDelete);
479-
}
480-
481-
// Left child case
482-
if (node1.HasLeftChild)
483-
{
484-
node2 = node1.LeftChild;
478+
x = nodeToDelete.LeftChild;
479+
Transplant(nodeToDelete, nodeToDelete.LeftChild);
485480
}
486481
else
487482
{
488-
node2 = node1.RightChild;
489-
}
490-
491-
// If node2 is not null, copy parent references
492-
if (node2 != null)
493-
node2.Parent = node1.Parent;
483+
// Node Y moves to the original node X position in the tree.
484+
var y = (RedBlackTreeNode<TKey>)_findMinNode(nodeToDelete.RightChild);
485+
x = y.RightChild;
494486

495-
if (node1.Parent != null)
496-
{
497-
if (node1.IsLeftChild)
487+
if (y.Parent == nodeToDelete)
498488
{
499-
node1.Parent.LeftChild = node2;
489+
if (x != null)
490+
{
491+
x.Parent = y;
492+
}
500493
}
501494
else
502495
{
503-
node1.Parent.RightChild = node2;
496+
Transplant(y, y.RightChild);
497+
y.RightChild = nodeToDelete.RightChild;
498+
y.RightChild.Parent = y;
504499
}
505-
}
506-
else
507-
{
508-
Root = node2;
509-
Root.Parent = null;
510-
}
511500

512-
// Swap values
513-
if (!node1.IsEqualTo(nodeToDelete))
514-
{
515-
nodeToDelete.Value = node1.Value;
501+
Transplant(nodeToDelete, y);
502+
y.LeftChild = nodeToDelete.LeftChild;
503+
y.LeftChild.Parent = y;
504+
y.Color = nodeToDelete.Color;
505+
506+
if (Root == nodeToDelete)
507+
{
508+
Root = y;
509+
Root.Parent = null;
510+
}
516511
}
517512

518-
// Adjust the Red-Black Tree rules
519-
if (node1.Color == RedBlackTreeColors.Black && node2 != null)
513+
if (nodeToDelete.Color == RedBlackTreeColors.Black)
520514
{
521-
_adjustTreeAfterRemoval(node2);
522-
Root.Color = RedBlackTreeColors.Black;
515+
_adjustTreeAfterRemoval(x);
523516
}
524517

525-
// Decrement the count
526518
base._count--;
527519

528520
return true;
529521
}
530522

523+
/// <summary>
524+
/// Insert one subtree in the place of the other in his parent.
525+
/// </summary>
526+
/// <param name="replaced">Subtree replaced by <param name="replacement">.</param></param>
527+
/// <param name="replacement">Subtree replaces <param name="replaced">.</param></param>
528+
private void Transplant(RedBlackTreeNode<TKey> replaced, RedBlackTreeNode<TKey> replacement)
529+
{
530+
if (replaced.Parent == null)
531+
{
532+
this.Root = replacement;
533+
}
534+
else if (replaced == replaced.Parent.LeftChild)
535+
{
536+
replaced.Parent.LeftChild = replacement;
537+
}
538+
else
539+
{
540+
replaced.Parent.RightChild = replacement;
541+
}
531542

543+
if (replacement != null)
544+
{
545+
replacement.Parent = replaced.Parent;
546+
}
547+
}
532548
/*************************************************************************************************/
533549

534550

0 commit comments

Comments
 (0)