Skip to content

Commit c471301

Browse files
authored
Merge branch 'master' into feature/tsp-traveling-salesman-problem
2 parents a9fd194 + a361c66 commit c471301

File tree

4 files changed

+160
-1
lines changed

4 files changed

+160
-1
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: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace Algorithms.Problems.JobScheduling;
6+
7+
/// <summary>
8+
/// Implements the greedy algorithm for Interval Scheduling.
9+
/// Finds the maximum set of non-overlapping jobs.
10+
/// </summary>
11+
public static class IntervalSchedulingSolver
12+
{
13+
/// <summary>
14+
/// Returns the maximal set of non-overlapping jobs.
15+
/// </summary>
16+
/// <param name="jobs">List of jobs to schedule.</param>
17+
/// <returns>List of selected jobs (maximal set).</returns>
18+
public static List<Job> Schedule(IEnumerable<Job> jobs)
19+
{
20+
if (jobs == null)
21+
{
22+
throw new ArgumentNullException(nameof(jobs));
23+
}
24+
25+
// Sort jobs by their end time (earliest finish first)
26+
var sortedJobs = jobs.OrderBy(j => j.End).ToList();
27+
var result = new List<Job>();
28+
int lastEnd = int.MinValue;
29+
30+
foreach (var job in sortedJobs)
31+
{
32+
// If the job starts after the last selected job ends, select it
33+
if (job.Start >= lastEnd)
34+
{
35+
result.Add(job);
36+
lastEnd = job.End;
37+
}
38+
}
39+
40+
return result;
41+
}
42+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Algorithms.Problems.JobScheduling;
2+
3+
/// <summary>
4+
/// Represents a single job with a start and end time.
5+
/// </summary>
6+
public record Job(int Start, int End);

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,8 @@ find more than one implementation for the same objective but using different alg
249249
* [Levenshtein Distance](./Algorithms/Problems/DynamicProgramming/LevenshteinDistance/LevenshteinDistance.cs)
250250
* [Traveling Salesman Problem (TSP)](./Algorithms/Problems/TravelingSalesman/TravelingSalesmanSolver.cs)
251251
* [Brute-force and Nearest Neighbor algorithms](./Algorithms/Problems/TravelingSalesman/TravelingSalesmanSolver.cs)
252-
252+
* [Job Scheduling](./Algorithms/Problems/JobScheduling)
253+
* [Interval Scheduling (Greedy)](./Algorithms/Problems/JobScheduling/IntervalSchedulingSolver.cs)
253254
* [Data Structures](./DataStructures)
254255
* [Bag](./DataStructures/Bag)
255256
* [Bit Array](./DataStructures/BitArray.cs)

0 commit comments

Comments
 (0)