Skip to content

Commit 6017b94

Browse files
committed
Add Interval Scheduling (Greedy Algorithm) and Tests
1 parent b47cdc9 commit 6017b94

File tree

4 files changed

+162
-0
lines changed

4 files changed

+162
-0
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Algorithms.Problems.JobScheduling;
4+
5+
namespace Algorithms.Tests.Problems.JobScheduling;
6+
7+
public class IntervalSchedulingSolverTests
8+
{
9+
[Test]
10+
public void Schedule_ReturnsEmpty_WhenNoJobs()
11+
{
12+
var result = IntervalSchedulingSolver.Schedule(new List<Job>());
13+
Assert.That(result, Is.Empty);
14+
}
15+
16+
[Test]
17+
public void Schedule_ReturnsSingleJob_WhenOnlyOneJob()
18+
{
19+
var jobs = new List<Job> { new Job(1, 3) };
20+
var result = IntervalSchedulingSolver.Schedule(jobs);
21+
Assert.That(result, Has.Count.EqualTo(1));
22+
Assert.That(result[0], Is.EqualTo(jobs[0]));
23+
}
24+
25+
[Test]
26+
public void Schedule_ThrowsArgumentNullException_WhenJobsIsNull()
27+
{
28+
Assert.Throws<ArgumentNullException>(() => IntervalSchedulingSolver.Schedule(jobs: null!));
29+
}
30+
31+
[Test]
32+
public void Schedule_SelectsJobsWithEqualEndTime()
33+
{
34+
var jobs = new List<Job>
35+
{
36+
new Job(1, 4),
37+
new Job(2, 4),
38+
new Job(4, 6)
39+
};
40+
var result = IntervalSchedulingSolver.Schedule(jobs);
41+
Assert.That(result, Has.Count.EqualTo(2));
42+
Assert.That(result, Does.Contain(new Job(1, 4)));
43+
Assert.That(result, Does.Contain(new Job(4, 6)));
44+
}
45+
46+
[Test]
47+
public void Schedule_SelectsJobStartingAtLastEnd()
48+
{
49+
var jobs = new List<Job>
50+
{
51+
new Job(1, 3),
52+
new Job(3, 5),
53+
new Job(5, 7)
54+
};
55+
var result = IntervalSchedulingSolver.Schedule(jobs);
56+
Assert.That(result, Has.Count.EqualTo(3));
57+
Assert.That(result[0], Is.EqualTo(jobs[0]));
58+
Assert.That(result[1], Is.EqualTo(jobs[1]));
59+
Assert.That(result[2], Is.EqualTo(jobs[2]));
60+
}
61+
62+
[Test]
63+
public void Schedule_HandlesJobsWithNegativeTimes()
64+
{
65+
var jobs = new List<Job>
66+
{
67+
new Job(-5, -3),
68+
new Job(-2, 1),
69+
new Job(0, 2)
70+
};
71+
var result = IntervalSchedulingSolver.Schedule(jobs);
72+
Assert.That(result, Has.Count.EqualTo(2));
73+
Assert.That(result, Does.Contain(new Job(-5, -3)));
74+
Assert.That(result, Does.Contain(new Job(-2, 1)));
75+
}
76+
77+
[Test]
78+
public void Schedule_SelectsNonOverlappingJobs()
79+
{
80+
var jobs = new List<Job>
81+
{
82+
new Job(1, 4),
83+
new Job(3, 5),
84+
new Job(0, 6),
85+
new Job(5, 7),
86+
new Job(8, 9),
87+
new Job(5, 9)
88+
};
89+
var result = IntervalSchedulingSolver.Schedule(jobs);
90+
// Expected: (1,4), (5,7), (8,9)
91+
Assert.That(result, Has.Count.EqualTo(3));
92+
Assert.That(result, Does.Contain(new Job(1, 4)));
93+
Assert.That(result, Does.Contain(new Job(5, 7)));
94+
Assert.That(result, Does.Contain(new Job(8, 9)));
95+
}
96+
97+
[Test]
98+
public void Schedule_HandlesFullyOverlappingJobs()
99+
{
100+
var jobs = new List<Job>
101+
{
102+
new Job(1, 5),
103+
new Job(2, 6),
104+
new Job(3, 7)
105+
};
106+
var result = IntervalSchedulingSolver.Schedule(jobs);
107+
// Only one job can be selected
108+
Assert.That(result, Has.Count.EqualTo(1));
109+
}
110+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
namespace Algorithms.Problems.JobScheduling;
2+
3+
/// <summary>
4+
/// Implements the greedy algorithm for Interval Scheduling.
5+
/// Finds the maximum set of non-overlapping jobs.
6+
/// </summary>
7+
public static class IntervalSchedulingSolver
8+
{
9+
/// <summary>
10+
/// Returns the maximal set of non-overlapping jobs.
11+
/// </summary>
12+
/// <param name="jobs">List of jobs to schedule.</param>
13+
/// <returns>List of selected jobs (maximal set).</returns>
14+
public static List<Job> Schedule(IEnumerable<Job> jobs)
15+
{
16+
if (jobs == null)
17+
{
18+
throw new ArgumentNullException(nameof(jobs));
19+
}
20+
21+
// Sort jobs by their end time (earliest finish first)
22+
var sortedJobs = jobs.OrderBy(j => j.End).ToList();
23+
var result = new List<Job>();
24+
int lastEnd = int.MinValue;
25+
26+
foreach (var job in sortedJobs)
27+
{
28+
// If the job starts after the last selected job ends, select it
29+
if (job.Start >= lastEnd)
30+
{
31+
result.Add(job);
32+
lastEnd = job.End;
33+
}
34+
}
35+
36+
return result;
37+
}
38+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace Algorithms.Problems.JobScheduling;
6+
7+
/// <summary>
8+
/// Represents a single job with a start and end time.
9+
/// </summary>
10+
public record Job(int Start, int End);

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,10 @@ find more than one implementation for the same objective but using different alg
245245
* [Dynamic Programming](./Algorithms/Problems/DynamicProgramming)
246246
* [Coin Change](./Algorithms/Problems/DynamicProgramming/CoinChange/DynamicCoinChangeSolver.cs)
247247
* [Levenshtein Distance](./Algorithms/Problems/DynamicProgramming/LevenshteinDistance/LevenshteinDistance.cs)
248+
* [Job Scheduling](./Algorithms/Problems/JobScheduling)
249+
* [Interval Scheduling (Greedy)](./Algorithms/Problems/JobScheduling/IntervalSchedulingSolver.cs)
250+
* [Weighted Interval Scheduling (DP)](./Algorithms/Problems/JobScheduling/WeightedIntervalSchedulingSolver.cs)
251+
* **Applications:** Work schedule management, room booking, production optimization, and more.
248252

249253
* [Data Structures](./DataStructures)
250254
* [Bag](./DataStructures/Bag)

0 commit comments

Comments
 (0)