Skip to content

Commit 6c220ce

Browse files
authored
Add bag datastructure (#531)
1 parent 178d7f9 commit 6c220ce

File tree

4 files changed

+274
-0
lines changed

4 files changed

+274
-0
lines changed

DataStructures.Tests/BagTests.cs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using DataStructures.Bag;
4+
using FluentAssertions;
5+
using NUnit.Framework;
6+
7+
namespace DataStructures.Tests;
8+
9+
internal class BagTests
10+
{
11+
[Test]
12+
public void Add_ShouldIncreaseCount()
13+
{
14+
// Arrange & Act
15+
var bag = new Bag<int>
16+
{
17+
1,
18+
2,
19+
1
20+
};
21+
22+
// Assert
23+
bag.Count.Should().Be(3);
24+
}
25+
26+
[Test]
27+
public void Add_ShouldHandleDuplicates()
28+
{
29+
// Arrange & Act
30+
var bag = new Bag<string>
31+
{
32+
"apple",
33+
"apple"
34+
};
35+
36+
// Assert
37+
bag.Count.Should().Be(2);
38+
bag.Should().Contain("apple");
39+
}
40+
41+
[Test]
42+
public void Clear_ShouldEmptyTheBag()
43+
{
44+
// Arrange
45+
var bag = new Bag<int>
46+
{
47+
1,
48+
2
49+
};
50+
51+
// Act
52+
bag.Clear();
53+
54+
// Assert
55+
bag.IsEmpty().Should().BeTrue();
56+
bag.Count.Should().Be(0);
57+
}
58+
59+
[Test]
60+
public void IsEmpty_ShouldReturnTrueForEmptyBag()
61+
{
62+
// Arrange
63+
var bag = new Bag<int>();
64+
65+
// Act & Assert
66+
bag.IsEmpty().Should().BeTrue();
67+
}
68+
69+
[Test]
70+
public void IsEmpty_ShouldReturnFalseForNonEmptyBag()
71+
{
72+
// Arrange
73+
var bag = new Bag<int>
74+
{
75+
1
76+
};
77+
78+
// Act & Assert
79+
bag.IsEmpty().Should().BeFalse();
80+
}
81+
82+
[Test]
83+
public void GetEnumerator_ShouldIterateAllItems()
84+
{
85+
// Arrange
86+
var bag = new Bag<int>
87+
{
88+
1,
89+
2,
90+
1
91+
};
92+
93+
// Act
94+
var items = bag.ToList();
95+
96+
// Assert
97+
items.Count.Should().Be(3);
98+
items.Should().Contain(1);
99+
items.Should().Contain(2);
100+
}
101+
102+
[Test]
103+
public void Count_ShouldReturnZeroForEmptyBag()
104+
{
105+
// Arrange
106+
var bag = new Bag<int>();
107+
108+
// Act & Assert
109+
bag.Count.Should().Be(0);
110+
}
111+
112+
[Test]
113+
public void Count_ShouldReturnCorrectCount()
114+
{
115+
// Arrange
116+
var bag = new Bag<int>
117+
{
118+
1,
119+
2,
120+
1
121+
};
122+
123+
// Act & Assert
124+
bag.Count.Should().Be(3);
125+
}
126+
127+
[Test]
128+
public void IEnumerableGetEnumerator_YieldsAllItemsWithCorrectMultiplicity()
129+
{
130+
// Arrange
131+
var bag = new Bag<string>
132+
{
133+
"apple",
134+
"banana",
135+
"apple"
136+
};
137+
var genericBag = bag as System.Collections.IEnumerable;
138+
139+
// Act
140+
var enumerator = genericBag.GetEnumerator();
141+
var items = new List<object>();
142+
while (enumerator.MoveNext())
143+
{
144+
items.Add(enumerator.Current!);
145+
}
146+
147+
// Assert
148+
items.Count(i => (string)i == "apple").Should().Be(2);
149+
items.Count(i => (string)i == "banana").Should().Be(1);
150+
items.Count.Should().Be(3);
151+
items.Should().BeEquivalentTo(["apple", "apple", "banana"]);
152+
}
153+
}

DataStructures/Bag/Bag.cs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
4+
namespace DataStructures.Bag;
5+
6+
/// <summary>
7+
/// Implementation of a Bag (or multiset) data structure using a basic linked list.
8+
/// </summary>
9+
/// <remarks>
10+
/// A bag (or multiset, or mset) is a modification of the concept of a set that, unlike a set, allows for multiple instances for each of its elements.
11+
/// The number of instances given for each element is called the multiplicity of that element in the multiset.
12+
/// As a consequence, an infinite number of multisets exist that contain only elements a and b, but vary in the multiplicities of their elements.
13+
/// See https://en.wikipedia.org/wiki/Multiset for more information.
14+
/// </remarks>
15+
/// <typeparam name="T">Generic Type.</typeparam>
16+
public class Bag<T> : IEnumerable<T> where T : notnull
17+
{
18+
private BagNode<T>? head;
19+
private int totalCount;
20+
21+
/// <summary>
22+
/// Initializes a new instance of the <see cref="Bag{T}" /> class.
23+
/// </summary>
24+
public Bag()
25+
{
26+
head = null;
27+
totalCount = 0;
28+
}
29+
30+
/// <summary>
31+
/// Adds an item to the bag. If the item already exists, increases its multiplicity.
32+
/// </summary>
33+
public void Add(T item)
34+
{
35+
// If the bag is empty, create the first node
36+
if (head == null)
37+
{
38+
head = new BagNode<T>(item);
39+
totalCount = 1;
40+
return;
41+
}
42+
43+
// Check if item already exists
44+
var current = head;
45+
BagNode<T>? previous = null;
46+
47+
while (current != null)
48+
{
49+
if (EqualityComparer<T>.Default.Equals(current.Item, item))
50+
{
51+
current.Multiplicity++;
52+
totalCount++;
53+
return;
54+
}
55+
56+
previous = current;
57+
current = current.Next;
58+
}
59+
60+
previous!.Next = new BagNode<T>(item);
61+
totalCount++;
62+
}
63+
64+
/// <summary>
65+
/// Clears the bag.
66+
/// </summary>
67+
public void Clear()
68+
{
69+
head = null;
70+
totalCount = 0;
71+
}
72+
73+
/// <summary>
74+
/// Gets the number of items in the bag.
75+
/// </summary>
76+
public int Count => totalCount;
77+
78+
/// <summary>
79+
/// Returns a boolean indicating whether the bag is empty.
80+
/// </summary>
81+
public bool IsEmpty() => head == null;
82+
83+
/// <summary>
84+
/// Returns an enumerator that iterates through the bag.
85+
/// </summary>
86+
public IEnumerator<T> GetEnumerator()
87+
{
88+
var current = head;
89+
90+
while (current != null)
91+
{
92+
// Yield the item as many times as its multiplicity, pretending they are separate items
93+
for (var i = 0; i < current.Multiplicity; i++)
94+
{
95+
yield return current.Item;
96+
}
97+
98+
current = current.Next;
99+
}
100+
}
101+
102+
/// <summary>
103+
/// Returns an enumerator that iterates through the bag.
104+
/// </summary>
105+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
106+
}

DataStructures/Bag/BagNode.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace DataStructures.Bag;
2+
3+
/// <summary>
4+
/// Generic node class for Bag.
5+
/// </summary>
6+
/// <typeparam name="T">A type for node.</typeparam>
7+
public class BagNode<T>(T item)
8+
{
9+
public T Item { get; } = item;
10+
11+
public int Multiplicity { get; set; } = 1;
12+
13+
public BagNode<T>? Next { get; set; }
14+
}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ find more than one implementation for the same objective but using different alg
247247
* [Levenshtein Distance](./Algorithms/Problems/DynamicProgramming/LevenshteinDistance/LevenshteinDistance.cs)
248248

249249
* [Data Structures](./DataStructures)
250+
* [Bag](./DataStructures/Bag)
250251
* [Bit Array](./DataStructures/BitArray.cs)
251252
* [Timeline](./DataStructures/Timeline.cs)
252253
* [Segment Trees](./DataStructures/SegmentTrees)

0 commit comments

Comments
 (0)