Course Schedule IV

Problem Statement

  • Aapko numCourses courses lene hain, jo 0 se leke numCourses - 1 tak label kiye gaye hain. Aapko ek array prerequisites diya gaya hai, jo batata hai ki kis course ko pehle lena zaroori hai.

    Prerequisites: prerequisites[i] = [ai, bi] ka matlab hai ki agar aap course bi lena chahte hain, toh aapko pehle course ai lena hoga.

    Indirect Prerequisites: Agar course a prerequisite hai course b ka, aur course b prerequisite hai course c ka, toh course a indirectly prerequisite ho gaya course c ka.

    Queries: Aapko ek queries array diya gaya hai, jahan queries[j] = [uj, vj] indicate karta hai ki aapko batana hai ki course uj prerequisite hai ya nahi course vj ka.

    Aapko ek boolean array answer return karna hai, jahan answer[j] indicates whether course uj is a prerequisite of course vj or not.

    Examples:

    Example 1:

    • Input: numCourses = 2, prerequisites = [[1,0]], queries = [[0,1],[1,0]]

    • Output: [false,true]

    • Explanation: pair [1, 0] ka matlab hai ki course 1 ko pehle lena zaroori hai course 0 ke liye. Course 0 kaise prerequisite nahi hai course 1 ka, lekin iska opposite sach hai.

    Example 2:

    • Input: numCourses = 2, prerequisites = [], queries = [[1,0],[0,1]]

    • Output: [false,false]

    • Explanation: Koi bhi prerequisites nahi hain, isliye har course independent hai.

    Example 3:

    • Input: numCourses = 3, prerequisites = [[1,2],[1,0],[2,0]], queries = [[1,0],[1,2]]

    • Output: [true,true]

    • Explanation: Course 1 prerequisite hai course 0 ka, aur course 2 prerequisite hai course 0 ka. Isliye, course 1 indirectly prerequisite hai course 0 ka.

    Constraints:

    • 2 <= numCourses <= 100

    • 0 <= prerequisites.length <= (numCourses * (numCourses - 1) / 2)

    • prerequisites[i].length == 2

    • 0 <= ai, bi <= numCourses - 1

    • ai != bi

    • Sare pairs prerequisites[i] unique hain.

    • Prerequisites graph mein koi cycles nahi hain.

    • 1 <= queries.

    • length <= 104

    • 0 <= ui, vi <= numCourses - 1

    • ui != vi

Course Schedule IV

Socho courses ko ek family tree ki tarah, jahan har course ke "parent" courses (prerequisites) hote hain:

Agar course A, course B ka parent hai, aur course B, course C ka parent hai, to course A, course C ka "grandparent" ho gaya!

Matlab agar aapko course C lena hai, pehle aapko course B lena padega, aur usse pehle course A lena padega.

Relationship Tracking: Is problem ko efficiently solve karne ke liye:

Track Direct Prerequisites: Pehle direct prerequisites ko track karte hain. Yaani, har course ka immediate parent course kaunsa hai.

Track Indirect Relationships: Fir indirect relationships ko track karte hain. Yaani, kaunse courses kis courses ke ancestor hain (jaise grandparents ke jaisa).

Step 1: Building the Graph πŸ—οΈ Adjacency List create karte hain: Har course ke saath uske connected courses ko list mein add karte hain. Ye list bilkul contact list ki tarah hoti hai, jahan har person ke friends ke numbers hote hain. πŸ“±

cpp vector> adj(numCourses); // Adjacency list vector indegree(numCourses, 0); // Indegree array for(auto p : prerequisites) { adj[p[0]].push_back(p[1]); indegree[p[1]]++; } Har course ke prerequisites count karte hain (indegree). πŸ”’

Step 2: Finding Starting Points 🎯 Queue banate hain un courses ke liye jo kisi aur course ke prerequisites nahi hain (like the oldest ancestors πŸ‘΄).cpp queue q; for (int i = 0; i < numCourses; i++) { if (indegree[i] == 0) { q.push(i); } }

In courses ko ek queue mein daal dete hain, bilkul coffee shop ki line ki tarah. β˜•

Step 3: Processing Courses πŸ”„ Map use karte hain taaki har course ke saare prerequisites store kar sakein. πŸ—ΊοΈ

cpp unordered_map> mp; while (!q.empty()) { int curr = q.front(); q.pop(); for (auto next : adj[curr]) { mp[next].insert(curr); // Direct prerequisite for (auto pre : mp[curr]) { mp[next].insert(pre); // Indirect prerequisites } indegree[next]--; // Update indegree count if (indegree[next] == 0) { q.push(next); } } }

Har course ko process karte time uske dependent courses ko as a prerequisite add karte hain, aur saare inherited prerequisites bhi pass karte hain. 🧬

Step 4: Answering Queries πŸ’‘ Result Array banate hain jahan har query ka jawab ek map ke through check karte hain. πŸ”

cpp vector res; for (auto q : queries) { res.push_back(mp[q[1]].count(q[0])); }

Bilkul family tree check karne ke tarah, har query ke answer ko res array mein add karte hain. πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦

Time Complexity:
  • Building graph: O(P) jahan P hai prerequisites ka length πŸ“Š

    • Har prerequisite ko ek baar traverse karna hota hai to build the graph, isliye complexity O(P) hoti hai.

  • Processing courses: O(V * E) jahan:

    • V (Vertices) = number of courses πŸ“š

    • E (Edges) = number of prerequisites edges πŸ”—

    • Har course aur uske edges ko process karne mein O(V * E) time lagta hai.

  • Answering queries: O(Q) jahan Q hai queries ka length πŸ”

    • Har query ko ek baar check karna hota hai, isliye O(Q) hoti hai.

Total: O(V * E + Q) ⚑

  • Yeh overall time complexity hai kyunki hume pehle graph banana hota hai, phir courses ko process karna hota hai aur queries ka jawab dena hota hai. Sabko combine karke total complexity O(V * E + Q) hoti hai.

Space Complexity:
  • Adjacency List: O(P) πŸ“

    • Graph ke edges ko store karne ke liye memory O(P).

  • Queue: O(V) πŸ“‹

    • Courses ko process karne ke liye queue use karte hain, which needs O(V) space.

  • Prerequisites Map: O(V * E) πŸ—ƒοΈ

    • Har course ke prerequisites ko store karte hain, jo need karta hai O(V * E) space.

Total: O(V * E) πŸ’Ώ

  • Yeh overall space complexity hai kyunki hume graph store karna hota hai aur prerequisites track karni hoti hain. Sabko combine karke total space complexity O(V * E) hoti hai.

Solution

				
					/** 
 * @param {number} numCourses 
 * @param {number[][]} prerequisites 
 * @param {number[][]} queries 
 * @return {boolean[]} 
 */
var checkIfPrerequisite = function(numCourses, prerequisites, queries) {
    // Adjacency list banate hain har course ke prerequisites ko store karne ke liye
    const adj = Array.from({length: numCourses}, () => []);
    const indegree = new Array(numCourses).fill(0); // Indegree array initialize karte hain

    // Prerequisites ko adjacency list aur indegree array mein add karte hain
    for(let p of prerequisites) {
        adj[p[0]].push(p[1]);
        indegree[p[1]]++;
    }

    const q = [];
    // Queue ko un courses se initialize karte hain jinke koi prerequisites nahi hain (leaf nodes)
    for(let i = 0; i < numCourses; i++) {
        if(indegree[i] === 0) {
            q.push(i);
        }
    }

    const mp = new Map(); // Map to store all prerequisites for each course
    // Har course ka ek new set create karte hain
    for(let i = 0; i < numCourses; i++) {
        mp.set(i, new Set());
    }

    // Processing the courses using Kahn's algorithm
    while(q.length > 0) {
        const curr = q.shift(); // Queue se current course nikalte hain
        for(let next of adj[curr]) { // Har adjacent course ke liye
            mp.get(next).add(curr); // Direct prerequisite add karte hain
            for(let pre of mp.get(curr)) { // Indirect prerequisites bhi add karte hain
                mp.get(next).add(pre);
            }
            indegree[next]--; // Indegree update karte hain
            if(indegree[next] === 0) { // Agar next course ke prerequisites complete ho gaye hain
                q.push(next); // Queue mein add karte hain
            }
        }
    }

    // Answering the queries by checking the map
    return queries.map(([u, v]) => mp.get(v).has(u)); // Agar prerequisite hai to true, otherwise false
};

				
			
Step 1: Graph Build karna

Pehle hum har course ke jitne bhi prerequisites hain unhe track karte hain. Iske liye hum adjacency list (jaise contact list) banate hain jaha har course ke connected courses ko list mein add karte hain. Saath hi, har course ke prerequisites count ko track karte hain using an indegree array.

Step 2: Starting Points Dhundhna

Aise courses find karte hain jinka koi prerequisite nahi hai (jaise family tree ke oldest ancestors) aur inhe ek queue mein add karte hain. Jaise queue mein log line mein khade hote hain, waise hi is queue mein courses ko add karte hain, jo subse pehle process honge.

Step 3: Processing Courses

Ek map create karte hain jismein har course ke saare prerequisites ko store karte hain.

  1. Queue se ek course nikalte hain aur uske saare connected courses ko traverse karte hain.

  2. Us course ke prerequisites ko directly aur inherited tarike se add karte hain.

  3. Us course ke prerequisites count ko update karte hain, aur agar prerequisites complete ho jayein to us course ko queue mein dubara dalte hain.

Step 4: Answering Queries

Queries ko process karte hain by checking the map. Har query mein yeh check karte hain ki ek particular course, doosre course ka prerequisite hai ya nahi. Har query ka jawab ek result array mein store karte hain (true ya false).

Solution

				
					class Solution {
public:
    vector<bool> checkIfPrerequisite(int numCourses, vector<vector<int>>& prerequisites, vector<vector<int>>& queries) {
        // Adjacency list banate hain har course ke prerequisites ko store karne ke liye
        vector<vector<int>> adj(numCourses);
        vector<int> indegree(numCourses, 0); // Indegree array initialize karte hain
        for(auto p : prerequisites) {
            adj[p[0]].push_back(p[1]);
            indegree[p[1]]++; // Indegree update karte hain
        }
        
        queue<int> q;
        // Queue ko un courses se initialize karte hain jinke koi prerequisites nahi hain (leaf nodes)
        for(int i = 0; i < numCourses; i++) {
            if(indegree[i] == 0) {
                q.push(i);
            }
        }
        
        unordered_map<int, unordered_set<int>> mp; // Map to store all prerequisites for each course
        while(!q.empty()) {
            int curr = q.front();
            q.pop();
            for(auto next : adj[curr]) { // Har adjacent course ke liye
                mp[next].insert(curr); // Direct prerequisite add karte hain
                for(auto pre : mp[curr]) { // Indirect prerequisites bhi add karte hain
                    mp[next].insert(pre);
                }
                indegree[next]--; // Indegree update karte hain
                if(indegree[next] == 0) { // Agar next course ke prerequisites complete ho gaye hain
                    q.push(next); // Queue mein add karte hain
                }
            }
        }
        
        vector<bool> res; // Result array initialize karte hain
        for(auto q : queries) { // Har query ke liye
            res.push_back(mp[q[1]].count(q[0])); // Check karte hain prerequisite hai ya nahi
        }
        return res; // Result array return karte hain
    }
};

				
			
    • Step 1: Building the Graph
      • Prerequisite List Creation: Sabse pehle, har course ke prerequisites ko store karte hain in a form of a list. Iss list ko adjacency list kehte hain, jismein har course ke connected courses ko store karte hain.

      • Count Indegree: Har course ke prerequisites count karte hain using an indegree array. Matlab, har course kitne other courses par dependent hai, yeh store kiya jaata hai.

      Step 2: Finding Starting Points
      • Initialize Queue: Un courses ko identify karte hain jinke koi prerequisites nahi hain. Yani, yeh courses sabse pehle lene padenge. Inhe ek queue mein add karte hain, bilkul line mein khade hone ki tarah.

      Step 3: Processing Courses
      • Use Map for Prerequisites: Ek map banate hain to store all the prerequisites for each course.

      • Breadth-First Search (BFS): Queue se courses nikalte hain aur unke adjacent courses ko process karte hain.

        • Direct Prerequisites: Har course ke direct prerequisites ko map mein add karte hain.

        • Indirect Prerequisites: Har inherited prerequisites ko bhi us course ke liye add karte hain, yani, ek course ke ancestors sabhi courses ke inherited traits pass karte hain.

        • Update Indegree: Adjacent courses ke prerequisites count ko update karte hain, aur agar ek course ka prerequisite fulfill ho jata hai to usse queue mein add karte hain.

      Step 4: Answering Queries
      • Result Calculation: Queries ko process karte hain by checking the map. Har query mein yeh check karte hain ki ek particular course doosre course ka prerequisite hai ya nahi. Har query ka jawab ek boolean array mein store karte hain aur return karte hain