Skip to content

Commit a07a998

Browse files
authored
Merge pull request #82 from PatrykOlejniczak/master
Fix RedBlackTree impl/test #81
2 parents afb8ef1 + 3ea1f87 commit a07a998

File tree

3 files changed

+612
-168
lines changed

3 files changed

+612
-168
lines changed

DataStructures/Trees/BinarySearchTreeNode.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-

2-
namespace DataStructures.Trees
1+
namespace DataStructures.Trees
32
{
43
/// <summary>
54
/// The binary search tree node.
@@ -61,6 +60,11 @@ public virtual bool HasLeftChild
6160
get { return (this.LeftChild != null); }
6261
}
6362

63+
/// <summary>
64+
/// Check if this node has only one child and whether it is the right child.
65+
/// </summary>
66+
public virtual bool HasOnlyRightChild => !this.HasLeftChild && this.HasRightChild;
67+
6468
/// <summary>
6569
/// Checks whether this node has right child.
6670
/// </summary>
@@ -69,6 +73,11 @@ public virtual bool HasRightChild
6973
get { return (this.RightChild != null); }
7074
}
7175

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

DataStructures/Trees/RedBlackTree.cs

Lines changed: 85 additions & 56 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>
@@ -14,7 +13,6 @@ public enum RedBlackTreeColors
1413
Black = 1
1514
};
1615

17-
1816
/// <summary>
1917
/// Red-Black Tree Data Structure.
2018
/// </summary>
@@ -29,6 +27,10 @@ public class RedBlackTree<TKey> : BinarySearchTree<TKey> where TKey : IComparabl
2927
internal set { base.Root = value; }
3028
}
3129

30+
private bool IsRoot(RedBlackTreeNode<TKey> node)
31+
{
32+
return node == this.Root;
33+
}
3234

3335
/// <summary>
3436
/// CONSTRUCTOR.
@@ -233,8 +235,8 @@ protected virtual void _adjustTreeAfterInsertion(RedBlackTreeNode<TKey> currentN
233235
// This is the simplest step: Basically recolor, and bubble up to see if more work is needed.
234236
if (_safeCheckIsRed(_safeGetSibling(currentNode.Parent)))
235237
{
236-
// If it has a sibling and it is red, then then it has a parent
237-
currentNode.Parent.Color = RedBlackTreeColors.Red;
238+
// If it has a sibling and it is black, then then it has a parent
239+
currentNode.Parent.Color = RedBlackTreeColors.Black;
238240

239241
// Color sibling of parent as black
240242
_safeUpdateColor(_safeGetSibling(currentNode.Parent), RedBlackTreeColors.Black);
@@ -455,80 +457,109 @@ protected override bool _remove(BSTNode<TKey> nodeToDelete)
455457
}
456458

457459
/// <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.
460+
/// The internal remove helper.
461+
/// Separated from the overriden version to avoid casting the objects from BSTNode to RedBlackTreeNode.
462+
/// This is called from the overriden _remove(BSTNode nodeToDelete) helper.
461463
/// </summary>
462464
protected bool _remove(RedBlackTreeNode<TKey> nodeToDelete)
463465
{
464466
if (nodeToDelete == null)
465-
return false;
466-
467-
// Temporary nodes
468-
RedBlackTreeNode<TKey> node1, node2;
469-
470-
// If nodeToDelete has either one child or no children at all
471-
if (!nodeToDelete.HasLeftChild || !nodeToDelete.HasRightChild)
472467
{
473-
node1 = nodeToDelete;
474-
}
475-
else
476-
{
477-
// nodeToDelete has two children
478-
node1 = (RedBlackTreeNode<TKey>)base._findNextLarger(nodeToDelete);
468+
return false;
479469
}
480470

481-
// Left child case
482-
if (node1.HasLeftChild)
471+
if (IsRoot(nodeToDelete) && !nodeToDelete.HasChildren)
483472
{
484-
node2 = node1.LeftChild;
473+
Root = null;
485474
}
486475
else
487476
{
488-
node2 = node1.RightChild;
489-
}
490-
491-
// If node2 is not null, copy parent references
492-
if (node2 != null)
493-
node2.Parent = node1.Parent;
477+
// X is the node we will replace with the nodeToDelete in the tree once we remove it.
478+
RedBlackTreeNode<TKey> x;
494479

495-
if (node1.Parent != null)
496-
{
497-
if (node1.IsLeftChild)
480+
if (!nodeToDelete.HasChildren)
498481
{
499-
node1.Parent.LeftChild = node2;
482+
x = nodeToDelete;
483+
Transplant(nodeToDelete, null);
500484
}
501-
else
485+
else if (nodeToDelete.HasOnlyRightChild)
502486
{
503-
node1.Parent.RightChild = node2;
487+
x = nodeToDelete.RightChild;
488+
Transplant(nodeToDelete, nodeToDelete.RightChild);
504489
}
505-
}
506-
else
507-
{
508-
Root = node2;
509-
Root.Parent = null;
510-
}
490+
else if (nodeToDelete.HasOnlyLeftChild)
491+
{
492+
x = nodeToDelete.LeftChild;
493+
Transplant(nodeToDelete, nodeToDelete.LeftChild);
494+
}
495+
else
496+
{
497+
// Y is the node we will replace with the X in the tree once we move it to the nodeToDelete position.
498+
var y = (RedBlackTreeNode<TKey>)_findMinNode(nodeToDelete.RightChild);
499+
x = y.RightChild;
511500

512-
// Swap values
513-
if (!node1.IsEqualTo(nodeToDelete))
514-
{
515-
nodeToDelete.Value = node1.Value;
516-
}
501+
if (y.Parent == nodeToDelete)
502+
{
503+
if (x != null)
504+
{
505+
x.Parent = y;
506+
}
507+
}
508+
else
509+
{
510+
Transplant(y, y.RightChild);
511+
y.RightChild = nodeToDelete.RightChild;
512+
y.RightChild.Parent = y;
513+
}
517514

518-
// Adjust the Red-Black Tree rules
519-
if (node1.Color == RedBlackTreeColors.Black && node2 != null)
520-
{
521-
_adjustTreeAfterRemoval(node2);
522-
Root.Color = RedBlackTreeColors.Black;
515+
Transplant(nodeToDelete, y);
516+
y.LeftChild = nodeToDelete.LeftChild;
517+
y.LeftChild.Parent = y;
518+
y.Color = nodeToDelete.Color;
519+
520+
if (Root == nodeToDelete)
521+
{
522+
Root = y;
523+
Root.Parent = null;
524+
}
525+
}
526+
527+
if (nodeToDelete.Color == RedBlackTreeColors.Black)
528+
{
529+
_adjustTreeAfterRemoval(x);
530+
}
523531
}
524532

525-
// Decrement the count
526533
base._count--;
527534

528535
return true;
529536
}
530537

538+
/// <summary>
539+
/// Insert one subtree in the place of the other in his parent.
540+
/// </summary>
541+
/// <param name="replaced">Subtree of node will be replaced by <param name="replacement">.</param></param>
542+
/// <param name="replacement">Subtree replaces <param name="replaced">.</param></param>
543+
private void Transplant(RedBlackTreeNode<TKey> replaced, RedBlackTreeNode<TKey> replacement)
544+
{
545+
if (replaced.Parent == null)
546+
{
547+
this.Root = replacement;
548+
}
549+
else if (replaced == replaced.Parent.LeftChild)
550+
{
551+
replaced.Parent.LeftChild = replacement;
552+
}
553+
else
554+
{
555+
replaced.Parent.RightChild = replacement;
556+
}
531557

558+
if (replacement != null)
559+
{
560+
replacement.Parent = replaced.Parent;
561+
}
562+
}
532563
/*************************************************************************************************/
533564

534565

@@ -629,7 +660,5 @@ public override void RemoveMax()
629660
// Invoke the internal remove node method.
630661
this._remove(node);
631662
}
632-
633663
}
634-
635664
}

0 commit comments

Comments
 (0)