diff --git a/cpu_scheduling_algorithms/non_preemptive_sjf_scheduling.cpp b/cpu_scheduling_algorithms/non_preemptive_sjf_scheduling.cpp
new file mode 100644
index 00000000000..e7d9d370960
--- /dev/null
+++ b/cpu_scheduling_algorithms/non_preemptive_sjf_scheduling.cpp
@@ -0,0 +1,316 @@
+/**
+ * @file
+ * @brief Implementation of SJF CPU scheduling algorithm
+ * @details
+ * shortest job first (SJF), also known as shortest job next (SJN), is a
+ * scheduling policy that selects for execution the waiting process with the
+ * smallest execution time. SJN is a non-preemptive algorithm. Shortest
+ * remaining time is a preemptive variant of SJN.
+ *
+ * detailed description on SJF scheduling
+ * Author : Lakshmi Srikumar
+ */
+
+#include /// for sorting
+#include /// for assert
+#include /// for formatting the output
+#include /// for IO operations
+#include /// for std::priority_queue
+#include /// random number generation
+#include /// for std::unordered_set
+#include /// for std::vector
+
+using std::cin;
+using std::cout;
+using std::endl;
+using std::get;
+using std::left;
+using std::make_tuple;
+using std::priority_queue;
+using std::tuple;
+using std::unordered_set;
+using std::vector;
+
+/**
+ * @brief Comparator function for sorting a vector
+ * @tparam S Data type of Process ID
+ * @tparam T Data type of Arrival time
+ * @tparam E Data type of Burst time
+ * @param t1 First tuplet1
+ * @param t2 Second tuplet2
+ * @returns true if t1 and t2 are in the CORRECT order
+ * @returns false if t1 and t2 are in the INCORRECT order
+ */
+template
+bool sortcol(tuple& t1, tuple& t2) {
+ if (get<1>(t1) < get<1>(t2) ||
+ (get<1>(t1) == get<1>(t2) && get<0>(t1) < get<0>(t2))) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @class Compare
+ * @brief Comparator class for priority queue
+ * @tparam S Data type of Process ID
+ * @tparam T Data type of Arrival time
+ * @tparam E Data type of Burst time
+ */
+template
+class Compare {
+ public:
+ /**
+ * @param t1 First tuple
+ * @param t2 Second tuple
+ * @brief A comparator function that checks whether to swap the two tuples
+ * or not.
+ *
+ * detailed description of comparator
+ * @returns true if the tuples SHOULD be swapped
+ * @returns false if the tuples SHOULDN'T be swapped
+ */
+ bool operator()(tuple& t1,
+ tuple& t2) {
+ // Compare burst times for SJF
+ if (get<2>(t2) < get<2>(t1)) {
+ return true;
+ }
+ // If burst times are the same, compare arrival times
+ else if (get<2>(t2) == get<2>(t1)) {
+ return get<1>(t2) < get<1>(t1);
+ }
+ return false;
+ }
+};
+
+/**
+ * @class SJF
+ * @brief Class which implements the SJF scheduling algorithm
+ * @tparam S Data type of Process ID
+ * @tparam T Data type of Arrival time
+ * @tparam E Data type of Burst time
+ */
+template
+class SJF {
+ /**
+ * Priority queue of schedules(stored as tuples) of processes.
+ * In each tuple
+ * @tparam 1st element: Process ID
+ * @tparam 2nd element: Arrival Time
+ * @tparam 3rd element: Burst time
+ * @tparam 4th element: Completion time
+ * @tparam 5th element: Turnaround time
+ * @tparam 6th element: Waiting time
+ */
+ priority_queue,
+ vector>,
+ Compare>
+ schedule;
+
+ // Stores final status of all the processes after completing the execution.
+ vector> result;
+
+ // Stores process IDs. Used for confirming absence of a process while it.
+ unordered_set idList;
+
+ public:
+ /**
+ * @brief Adds the process to the ready queue if it isn't already there
+ * @param id Process ID
+ * @param arrival Arrival time of the process
+ * @param burst Burst time of the process
+ * @returns void
+ *
+ */
+ void addProcess(S id, T arrival, E burst) {
+ // Add if a process with process ID as id is not found in idList.
+ if (idList.find(id) == idList.end()) {
+ tuple t =
+ make_tuple(id, arrival, burst, 0, 0, 0);
+ schedule.push(t);
+ idList.insert(id);
+ }
+ }
+
+ /**
+ * @brief Algorithm for scheduling CPU processes according to
+ * the Shortest Job First (SJF) scheduling algorithm.
+ *
+ * @details Non pre-emptive SJF is an algorithm that schedules processes
+ * based on the length of their burst times. The process with the smallest
+ * burst time is executed first.In a non-preemptive scheduling algorithm,
+ * once a process starts executing,it runs to completion without being
+ * interrupted.
+ *
+ * I used a min priority queue because it allows you to efficiently pick the
+ * process with the smallest burst time in constant time, by maintaining a
+ * priority order where the shortest burst process is always at the front.
+ *
+ * @returns void
+ */
+
+ vector> scheduleForSJF() {
+ // Variable to keep track of time elapsed so far
+ double timeElapsed = 0;
+
+ while (!schedule.empty()) {
+ tuple cur = schedule.top();
+
+ // If the current process arrived at time t2, the last process
+ // completed its execution at time t1, and t2 > t1.
+ if (get<1>(cur) > timeElapsed) {
+ timeElapsed += get<1>(cur) - timeElapsed;
+ }
+
+ // Add Burst time to time elapsed
+ timeElapsed += get<2>(cur);
+
+ // Completion time of the current process will be same as time
+ // elapsed so far
+ get<3>(cur) = timeElapsed;
+
+ // Turnaround time = Completion time - Arrival time
+ get<4>(cur) = get<3>(cur) - get<1>(cur);
+
+ // Waiting time = Turnaround time - Burst time
+ get<5>(cur) = get<4>(cur) - get<2>(cur);
+
+ // Turnaround time >= Burst time
+ assert(get<4>(cur) >= get<2>(cur));
+
+ // Waiting time is never negative
+ assert(get<5>(cur) >= 0);
+
+ result.push_back(cur);
+ schedule.pop();
+ }
+ return result;
+ }
+ /**
+ * @brief Utility function for printing the status of
+ * each process after execution
+ * @returns void
+ */
+
+ void printResult(
+ const vector>& processes) {
+ cout << std::setw(17) << left << "Process ID" << std::setw(17) << left
+ << "Arrival Time" << std::setw(17) << left << "Burst Time"
+ << std::setw(17) << left << "Completion Time" << std::setw(17)
+ << left << "Turnaround Time" << std::setw(17) << left
+ << "Waiting Time" << endl;
+
+ for (const auto& process : processes) {
+ cout << std::setprecision(2) << std::fixed << std::setw(17) << left
+ << get<0>(process) << std::setw(17) << left << get<1>(process)
+ << std::setw(17) << left << get<2>(process) << std::setw(17)
+ << left << get<3>(process) << std::setw(17) << left
+ << get<4>(process) << std::setw(17) << left << get<5>(process)
+ << endl;
+ }
+ }
+};
+
+/**
+ * @brief Computes the final status of processes after
+ * applying non-preemptive SJF scheduling
+ * @tparam S Data type of Process ID
+ * @tparam T Data type of Arrival time
+ * @tparam E Data type of Burst time
+ * @param input A vector of tuples containing Process ID, Arrival time, and
+ * Burst time
+ * @returns A vector of tuples containing Process ID, Arrival time, Burst time,
+ * Completion time, Turnaround time, and Waiting time
+ */
+template
+vector> get_final_status(
+ vector> input) {
+ // Sort the processes based on Arrival time and then Burst time
+ sort(input.begin(), input.end(), sortcol);
+
+ // Result vector to hold the final status of each process
+ vector> result(input.size());
+ double timeElapsed = 0;
+
+ for (size_t i = 0; i < input.size(); i++) {
+ // Extract Arrival time and Burst time
+ T arrival = get<1>(input[i]);
+ E burst = get<2>(input[i]);
+
+ // If the CPU is idle, move time to the arrival of the next process
+ if (arrival > timeElapsed) {
+ timeElapsed = arrival;
+ }
+
+ // Update timeElapsed by adding the burst time
+ timeElapsed += burst;
+
+ // Calculate Completion time, Turnaround time, and Waiting time
+ double completion = timeElapsed;
+ double turnaround = completion - arrival;
+ double waiting = turnaround - burst;
+
+ // Store the results in the result vector
+ result[i] = make_tuple(get<0>(input[i]), arrival, burst, completion,
+ turnaround, waiting);
+ }
+
+ return result;
+}
+
+/**
+ * @brief Self-test implementations
+ * @returns void
+ */
+static void test() {
+ // A vector to store the results of all processes across all test cases.
+ vector>
+ finalResult;
+
+ for (int i{}; i < 10; i++) {
+ std::random_device rd; // Seeding
+ std::mt19937 eng(rd());
+ std::uniform_int_distribution<> distr(1, 10);
+
+ uint32_t n = distr(eng);
+ SJF readyQueue;
+ vector>
+ input(n);
+
+ // Generate random arrival and burst times
+ for (uint32_t i{}; i < n; i++) {
+ get<0>(input[i]) = i;
+ get<1>(input[i]) = distr(eng); // Random arrival time
+ get<2>(input[i]) = distr(eng); // Random burst time
+ }
+
+ // Print processes before scheduling
+ cout << "Processes before SJF scheduling:" << endl;
+ readyQueue.printResult(input);
+
+ // Add processes to the queue
+ for (uint32_t i{}; i < n; i++) {
+ readyQueue.addProcess(get<0>(input[i]), get<1>(input[i]),
+ get<2>(input[i]));
+ }
+
+ // Perform SJF schedulings
+ auto finalResult = readyQueue.scheduleForSJF();
+
+ // Print processes after scheduling
+ cout << "\nProcesses after SJF scheduling:" << endl;
+ readyQueue.printResult(finalResult);
+ }
+ cout << "All the tests have successfully passed!" << endl;
+}
+
+/**
+ * @brief Main function
+ * @returns 0 on successful exit
+ */
+int main() {
+ test();
+ return 0;
+}