Category: Miscellaneous

  • Maximum Bipartite Matching

    maximum bipartite graph is a type of bipartite graph that has the highest possible number of edges between two sets of vertices.

    bipartite graph is a graph where the set of vertices can be divided into two groups, such that no two vertices within the same group are directly connected. The maximum bipartite graph is one where every vertex in the first group is connected to every vertex in the second group.

    If one group has m vertices and the other has n vertices, the total number of edges in the maximum bipartite graph is:

    Emax = m  n
    

    Properties of Maximum Bipartite Graphs

    Maximum bipartite graphs have the following properties:

    • It has two separate groups of vertices, and edges only connect vertices from different groups.
    • The total number of edges is the maximum possible, which is the product of the number of vertices in each group.
    • Since there are no connections within the same group, it never contains odd-length cycles.
    • The graph can always be colored using just two colorsone for each group of vertices.
    • Maximum bipartite graphs are useful in real-world applications like job assignmentsnetworking, and resource allocation.

    Example of Maximum Bipartite Graph

    Consider the following bipartite graph with four vertices:

    A -- B
    |    |
    C -- D
    

    In this example, the graph has two groups of vertices: {A, C} and {B, D}. The maximum bipartite graph would have the following edges:

    • A — B
    • A — D
    • C — B
    • C — D

    There are four edges in total, which is the maximum possible for this graph.

    Algorithms for Maximum Bipartite Matching

    There are several algorithms to find the maximum bipartite matching in a graph:

    • Hopcroft-Karp Algorithm
    • Ford-Fulkerson Algorithm
    • Hungarian Algorithm
    • Edmonds-Karp Algorithm
    • Augmenting Path Algorithm
    • Network Flow Algorithm

    Code to Find Maximum Bipartite Matching using Hopcroft-Karp Algorithm

    Now, let’s look at the steps to find the maximum bipartite matching in a graph using the Hopcroft-Karp algorithm:

    1. Create a bipartite graph with two sets of vertices.
    2. Initialize an empty matching between the two sets.
    3. While there is an augmenting path in the graph:
        a. Find an augmenting path using a matching algorithm.
        b. Update the matching by adding or removing edges along the path.
    4. Return the maximum matching found.
    

    Let’s look at an example of code to find the maximum bipartite matching in a graph using the Hopcroft-Karp algorithm:

    CC++JavaPython

    // C program to find maximum matching in a bipartite graph#include <stdio.h>#include <stdbool.h>#define M 6#define N 6
    
    bool bpm(int bpGraph[M][N],int u, bool seen[],int matchR[]){for(int v =0; v < N; v++){if((bpGraph[u][v]&&!seen[v])){
             seen[v]= true;if((matchR[v]<0||bpm(bpGraph, matchR[v], seen, matchR))){
                matchR[v]= u;return true;}}}return false;}intmaxBPM(int bpGraph[M][N]){int matchR[N];for(int i =0; i < N; i++)
          matchR[i]=-1;int result =0;for(int u =0; u < M; u++){
          bool seen[N];for(int i =0; i < N; i++)
             seen[i]= false;if(bpm(bpGraph, u, seen, matchR))
             result++;}return result;}intmain(){int bpGraph[M][N]={{0,1,1,0,0,0},{1,0,0,1,0,0},{0,0,1,0,0,0},{0,0,1,1,0,0},{0,0,0,0,0,0},{0,0,0,0,0,1}};printf("Maximum matching is %d\n",maxBPM(bpGraph));return0;}

    Output

    Maximum matching is 5
    

    Applications of Maximum Bipartite Matching

    Maximum bipartite matching algorithms have several real-world applications:

    • Job Assignments: Assigning tasks to workers based on their skills and availability.
    • Networking: Routing data packets through a network to optimize traffic flow.
    • Resource Allocation: Allocating resources like time, money, or equipment to maximize efficiency.
    • Marriage Matching: Pairing individuals based on preferences and compatibility.
    • Project Planning: Scheduling tasks and dependencies to complete projects on time.

    These applications demonstrate the importance of maximum bipartite matching algorithms in solving complex optimization problems.

    Time Complexity of Maximum Bipartite Matching

    The time complexity of finding the maximum bipartite matching in a graph using the Hopcroft-Karp algorithm is O(sqrt(V) * E), where V is the number of vertices and E is the number of edges in the graph. This algorithm is efficient for sparse graphs with a large number of vertices.

    Conclusion

    In this tutorial, we learned about maximum bipartite graphs and how to find the maximum bipartite matching in a graph using various algorithms. We also explored the properties and applications of maximum bipartite graphs in real-world scenarios. By understanding these concepts, you can solve a wide range of optimization problems efficiently.

  • Bellman-Ford Algorithm

    Bellman-Ford is a popular algorithm for finding the shortest path from a starting point (or “source”) to all other points in a graph, even if some edges have negative weights. While its not as fast as Dijkstras algorithm, it has a big advantage: it can handle graphs with negative edge weights.

    How does Bellman-Ford work?

    The way the Bellman-Ford algorithm works is pretty simple. It goes through all the edges of the graph multiple times and tries to improve the shortest path estimates. It “relaxes” the edges by updating the distances until they converge to the correct values.

    It continues to do this for all edges in the graph, ensuring the optimal solution.

    So, even though it might take a bit longer than Dijkstras, its a good tool when negative weights are involved!

    Alogrithm steps for Bellman-Ford

    Heres a step-by-step guide to how the Bellman-Ford algorithm works:

    • Initialization: Start by assigning a distance of 0 to the source node and infinity () to all other nodes.
    • Relaxation: Go through all the edges of the graph and relax them. Relaxing an edge means updating the distance of the destination node if a shorter path is found.
    • Repeat: Repeat the relaxation step for all edges in the graph. Keep doing this until no more improvements can be made.
    • Negative Cycle Detection: After the relaxation step, check for negative cycles. If there are negative cycles in the graph, the algorithm will detect them and report that there is no shortest path.

    Code for Bellman-Ford Algorithm

    Consider a simple graph with vertices and edges.

        (0)
        /   \
      10     15
      /       \
    (1)---5---(3)  
      \       /
       10    10
        \   /
         (4)
           \
            10
             \
              (5)
    

    Example

    Here’s an example code snippet for the Bellman-Ford Shortest Path algorithm in C, C++, Java, and Python:

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>#include <limits.h>#define MAX_VERTICES 6int graph[MAX_VERTICES][MAX_VERTICES]={{0,10,0,15,0,0},{0,0,0,5,10,0},{0,0,0,0,0,10},{0,0,0,0,0,5},{0,0,0,0,0,10},{0,0,0,0,0,0}};voidbellman_ford(int source){int distance[MAX_VERTICES];for(int i =0; i < MAX_VERTICES; i++){
          distance[i]= INT_MAX;}
       distance[source]=0;for(int i =0; i < MAX_VERTICES -1; i++){for(int u =0; u < MAX_VERTICES; u++){for(int v =0; v < MAX_VERTICES; v++){if(graph[u][v]!=0&& distance[u]!= INT_MAX && distance[u]+ graph[u][v]< distance[v]){
                   distance[v]= distance[u]+ graph[u][v];}}}}for(int u =0; u < MAX_VERTICES; u++){for(int v =0; v < MAX_VERTICES; v++){if(graph[u][v]!=0&& distance[u]!= INT_MAX && distance[u]+ graph[u][v]< distance[v]){printf("Graph contains negative weight cycle\n");return;}}}printf("Vertex   Distance from Source\n");for(int i =0; i < MAX_VERTICES; i++){if(distance[i]== INT_MAX)printf("%d \t\t INF\n", i);elseprintf("%d \t\t %d\n", i, distance[i]);}}intmain(){bellman_ford(0);return0;}

    Output

    Following is the output of the above code:

    Vertex   Distance from Source
    0 		 0
    1 		 10
    2 		 INF
    3 		 15
    4 		 20
    5 		 20
    

    Time Complexity of Bellman-Ford Algorithm

    • The time complexity of the Bellman-Ford algorithm is O(V*E), where V is the number of vertices and E is the number of edges in the graph.
    • The algorithm goes through all the edges of the graph V-1 times, relaxing them to find the shortest path.
    • If there are no negative cycles in the graph, the algorithm will converge to the correct shortest path after V-1 iterations.

    Applications of Bellman-Ford Algorithm

    The Bellman-Ford algorithm is used in various applications, including:

    • Routing Protocols: Bellman-Ford is used in routing protocols like RIP (Routing Information Protocol) to find the shortest path in computer networks.
    • Network Optimization: Its used in network optimization problems to find the shortest path between two points in a graph.
    • Resource Allocation: Bellman-Ford is used in resource allocation problems to find the most efficient way to allocate resources.
    • Graph Analysis: Its used in graph analysis to find the shortest path between two nodes in a graph.

    Conclusion

    In this tutorial, we learned about the Bellman Ford Shortest Path algorithm. It’s code, time complexity, and applications.

  • Convert Infix expression to Postfix expression

    Infix to postfix conversion rearranges math expressions to make them easier for computers to evaluate. In infix notation, operators are between operands (e.g., “A + B”). In postfix notation, operators come after operands (e.g., “A B +”). We use a stack to convert infix to postfix, ensuring the order of operations is preserved.

    What is Infix, Postfix, and Prefix?

    Before jumping into conversion, lets understand the three common types of expressions:

    • Infix: The operator is placed between operands. Example: A + B
    • Postfix (Reverse Polish Notation): The operator comes after the operands. Example: A B +
    • Prefix (Polish Notation): The operator comes before the operands. Example: + A B

    Postfix notation is useful because it eliminates the need for parentheses and follows a simple evaluation process using stacks.

    Why Use a Stack for Conversion?

    Stacks helps us to manage the order of operations (precedence) and parentheses correctly. When scanning an infix expression, we can use a stack to store operators and pop them based on precedence. This way, we can convert infix to postfix without losing the order of operations.

    Infix to Postfix Conversion Algorithm

    The algorithm for converting an infix expression to a postfix expression using a stack is:

    Read the expression from left to right.
    1. If an operand (A-Z, 0-9) is encountered, add it directly to the output.
    2. If an operator is encountered:
       1. Pop operators from the stack to the output if they have higher or equal precedence.
       2. Push the current operator onto the stack.
    3. If an opening parenthesis ( is found, push it onto the stack.
    4. If a closing parenthesis ) is found, pop operators from the stack to the output until an opening parenthesis is encountered.
    5. After scanning the entire expression, pop any remaining operators from the stack to the output.
    

    Operator Precedence

    Operators have different levels of precedence, which determine the order of evaluation:

    • ^ (Exponentiation) Highest precedence
    • *, / (Multiplication, Division) Higher precedence
    • +, - (Addition, Subtraction) Lowest precedence

    When two operators have the same precedence, we evaluate them based on associativity. Most operators are left-to-right associative, except exponentiation, which is right-to-left.

    Example: Convert Infix to Postfix

    Let’s convert the infix expression: A + B * C

    StepScanned CharacterStackPostfix Expression
    1AA
    2++A
    3B+A B
    4*+ *A B
    5C+ *A B C
    6A B C * +

    Final Postfix Expression: A B C * +

    Evaluating Postfix Expression

    Now that we have converted infix to postfix, lets see how to evaluate it:

    • Read the postfix expression from left to right.
    • If an operand is found, push it to the stack.
    • If an operator is found, pop the last two operands from the stack, apply the operation, and push the result back.
    • Continue until the expression is fully processed. The final value in the stack is the result.

    Code for Infix to Postfix Conversion

    Now that we already know the algorithm, lets see how to implement it in C, C++, Java and Python:

    CC++JavaPython

    #include <stdio.h>#include <string.h>#include <ctype.h>#define MAX 100char stack[MAX];int top =-1;voidpush(char item){if(top ==(MAX -1))printf("Stack Overflow\n");else
          stack[++top]= item;}charpop(){if(top ==-1){printf("Stack Underflow\n");return'\0';}return stack[top--];}intprecedence(char symbol){switch(symbol){case'^':return3;case'*':case'/':return2;case'+':case'-':return1;default:return0;}}voidinfixToPostfix(char infix[],char postfix[]){int i =0, j =0;char symbol;push('(');strcat(infix,")");while(infix[i]!='\0'){
          symbol = infix[i];if(symbol ==' '){
             i++;continue;}if(symbol =='(')push(symbol);elseif(isalnum(symbol))
             postfix[j++]= symbol;elseif(symbol ==')'){while(top !=-1&& stack[top]!='(')
                postfix[j++]=pop();pop();}else{while(top !=-1&&precedence(stack[top])>=precedence(symbol))
                postfix[j++]=pop();push(symbol);}
          i++;}
       postfix[j]='\0';}intmain(){char infix[MAX]="A + B * C";char postfix[MAX];printf("Infix Expression: %s\n", infix);infixToPostfix(infix, postfix);printf("Postfix Expression: %s\n", postfix);return0;}

    Output

    Following is the output of the above code:

    Enter Infix Expression: A + B * C
    Postfix Expression: ABC*+
    

    Applications of Postfix Notation

    Following are some common applications of postfix notation:

    • Used in compilers to evaluate expressions without needing parentheses.
    • Helps in designing calculators where stack-based evaluation is required.
    • Used in Expression Trees for easy computation.

    Conclusion

    Infix notation is how we naturally write mathematical expressions, but computers prefer postfix because its easier to process. We can use stack for easy Infix to Postfix conversion. Understanding this concept is crucial for solving problems related to expression evaluation and compiler design.