Job Scheduling with Deadlines in DSA Go - Time & Space Complexity
We want to understand how the time needed to schedule jobs grows as we add more jobs.
Specifically, how the steps increase when we try to fit jobs before their deadlines.
Analyze the time complexity of the following code snippet.
package main
import "sort"
type Job struct {
id int
deadline int
profit int
}
func JobScheduling(jobs []Job) []int {
sort.Slice(jobs, func(i, j int) bool { return jobs[i].profit > jobs[j].profit })
n := len(jobs)
result := make([]int, n)
slot := make([]bool, n)
for i := 0; i < n; i++ {
for j := min(n-1, jobs[i].deadline-1); j >= 0; j-- {
if !slot[j] {
slot[j] = true
result[j] = jobs[i].id
break
}
}
}
return result
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
This code schedules jobs by profit, placing each job in the latest free slot before its deadline.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Nested loops where the outer loop goes through each job and the inner loop searches for a free slot before the job's deadline.
- How many times: Outer loop runs n times (number of jobs), inner loop can run up to n times in worst case.
As the number of jobs grows, the code tries to place each job by checking slots backward from its deadline.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | Up to 100 checks |
| 100 | Up to 10,000 checks |
| 1000 | Up to 1,000,000 checks |
Pattern observation: The number of operations grows roughly with the square of the number of jobs.
Time Complexity: O(n2)
This means if you double the number of jobs, the time to schedule them roughly quadruples.
[X] Wrong: "The scheduling always takes linear time because we just loop through jobs once."
[OK] Correct: Each job may require searching backward through slots, which can take up to n steps, making the total work grow faster than just one loop.
Understanding how nested loops affect time helps you explain your approach clearly and shows you can reason about efficiency in real problems.
What if we used a data structure to find free slots faster? How would the time complexity change?
Practice
Job Scheduling with Deadlines problem?Solution
Step 1: Understand the problem objective
The problem aims to schedule jobs so that each job finishes before its deadline and the total profit is maximized.Step 2: Identify the correct goal
Maximizing total profit while respecting deadlines is the key goal, not just scheduling all jobs or ignoring deadlines.Final Answer:
Maximize the total profit by scheduling jobs before their deadlines -> Option CQuick Check:
Goal = Maximize profit before deadlines [OK]
- Ignoring deadlines and scheduling all jobs
- Trying to minimize jobs instead of maximizing profit
- Scheduling jobs in input order without sorting
Solution
Step 1: Understand the scheduling requirement
We need to quickly find and assign the latest free slot before a job's deadline.Step 2: Identify the suitable data structure
Disjoint Set (Union-Find) efficiently tracks and merges available slots, making it ideal for this task.Final Answer:
Disjoint Set (Union-Find) -> Option BQuick Check:
Best structure for slot tracking = Disjoint Set [OK]
- Using slices which require linear search
- Using maps which don't track slot unions
- Using linked lists which are inefficient for this
package main
import "fmt"
type Job struct {
id string
deadline int
profit int
}
func main() {
jobs := []Job{{"a", 2, 100}, {"b", 1, 19}, {"c", 2, 27}, {"d", 1, 25}, {"e", 3, 15}}
// Assume jobs are sorted by profit descending
scheduled := []string{"a", "c", "e"}
fmt.Println(scheduled)
}Solution
Step 1: Analyze the job list and sorting
Jobs are sorted by profit descending: a(100), c(27), d(25), b(19), e(15). The code assumes scheduled jobs are a, c, e.Step 2: Check feasibility of scheduled jobs
Job a (deadline 2), c (deadline 2), e (deadline 3) can be scheduled without conflict, maximizing profit.Final Answer:
["a", "c", "e"] -> Option AQuick Check:
Scheduled jobs = ["a", "c", "e"] [OK]
- Ignoring deadlines and picking all high profit jobs
- Confusing job order in output
- Assuming jobs b or d are scheduled incorrectly
func scheduleJobs(jobs []Job) []string {
sort.Slice(jobs, func(i, j int) bool {
return jobs[i].profit > jobs[j].profit
})
result := []string{}
slots := make([]bool, len(jobs))
for _, job := range jobs {
for j := job.deadline; j >= 0; j-- {
if !slots[j] {
slots[j] = true
result = append(result, job.id)
break
}
}
}
return result
}Solution
Step 1: Check loop index range for slot assignment
The inner loop runs from job.deadline down to 0, but slot indices start at 0, so job.deadline is out of range.Step 2: Correct loop start index
Loop should start at job.deadline - 1 to match zero-based indexing of slots array.Final Answer:
Loop index j should start from job.deadline - 1 -> Option DQuick Check:
Indexing starts at deadline-1 [OK]
- Starting loop at job.deadline causing index out of range
- Not sorting jobs by profit first
- Ignoring zero-based indexing in Go slices
Solution
Step 1: Sort jobs by profit descending
Order: J4(100), J2(80), J1(70), J5(40), J3(30).Step 2: Schedule jobs greedily before deadlines
J4 deadline=2 -> slot 2
J2 deadline=1 -> slot 1
J1 deadline=4 -> slot 4
J5 deadline=3 -> slot 3
J3 deadline=1 -> slot 1 taken, skipStep 3: Calculate total profit
100 + 80 + 70 + 40 = 290 (But only 4 slots available, all used)Step 4: Re-check slot assignment
Slots: 1(J2), 2(J4), 3(J5), 4(J1). Total profit = 80 + 100 + 40 + 70 = 290Final Answer:
290 -> Option AQuick Check:
Sum profits of scheduled jobs = 290 [OK]
- Scheduling low profit jobs first
- Ignoring deadline constraints
- Double booking the same slot
Ready to prove it?
Pass the test and this pattern moves unknown
