Graph Algorithms, Dynamic and Greedy Algorithms


Breadth First Search (BFS)


BFS (G, s)                   //Where G is the graph and s is the source node
      let Q be queue.
      Q.enqueue( s ) //Inserting s in queue until all its neighbour vertices are marked.

      mark s as visited.
      while ( Q is not empty)
           //Removing that vertex from queue,whose neighbour will be visited now
           v  =  Q.dequeue( )

          //processing all the neighbours of v  
          for all neighbours w of v in Graph G
               if w is not visited 
                        Q.enqueue( w )             //Stores w in Q to further visit its neighbour
                        mark w as visited.

The time complexity of BFS is O(V + E), where V is the number of nodes and E is the number of edges.

 

Depth First Search

 
    DFS-iterative (G, s):                                   
       //Where G is graph and s is source vertex
      let S be stack
      S.push( s )            //Inserting s in stack 
      mark s as visited.
      while ( S is not empty):
          //Pop a vertex from stack to visit next
          v  =  S.top( )
         S.pop( )
         //Push all the neighbours of v in stack that are not visited   
        for all neighbours w of v in Graph G:
            if w is not visited :
                     S.push( w )         
                    mark w as visited


    DFS-recursive(G, s):
        mark s as visited
        for all neighbours w of s in Graph G:
            if w is not visited:
                DFS-recursive(G, w)
) 
Time complexity : O(V+E) when implemented using an adjacency list.

Kruskal’s Algorithm

KRUSKAL(G):
A = ∅ 
foreach v ∈ G.V:
  MAKE-SET(v) //make every independent set of an vertex 
foreach (u, v) in G.E ordered by weight(u, v), increasing: 
              //edges ko ascending order me lagao
if FIND-SET(u) ≠ FIND-SET(v):
              // if they found in diff set 
 A = A ∪ {(u, v)} // union karo A se.
 UNION(u, v) // dono ko ek set me combine kardo
return A
Time Complexity :O(E log E)

Prim's Algorithm

  1. MST-PRIM(G, w, r) //Where G is graph and r is source vertex w is the adjacent nodes for each u in V [G] // each vertex in graph do key[u] ← ∞ // set all vertex to infinity π[u] ← NIL // set parent to nil key[r] ← 0 //set source to 0 Q ← V [G] //put all vertex in Queue while Q ≠ Ø //while Q is not empty do u ← EXTRACT-MIN(Q) //Extract min vertex from queue
  2. for each v in Adj[u] // for each vertex in adj of u do if v in Q and w(u, v) < key[v] then
  3. π[v] ← u
    key[v] ← w(u, v)
The time complexity is O(VlogV + ElogV) = O(ElogV)

Bellman Ford's Algorithm:

function bellmanFord(G, S)
 for each vertex V in G
   distance[V] <- infinite
   previous[V] <- NULL
 distance[S] <- 0
 for each vertex V in G    
  for each edge (U,V) in G
   tempDistance <- distance[U] + edge_weight(U, V)
   if tempDistance < distance[V]
      distance[V] <- tempDistance
      previous[V] <- U

 for each edge (U,V) in G
  If distance[U] + edge_weight(U, V) < distance[V}
   Error: Negative Cycle Exists

 return distance[], previous[]
Time Complexity: O(V E)

Djikstra's algorithm


function dijkstra(G, S)
    for each vertex V in G
        distance[V] <- infinite
        previous[V] <- NULL
        If V != S, add V to Priority Queue Q
    distance[S] <- 0
 
    while Q IS NOT EMPTY
        U <- Extract MIN from Q
        for each unvisited neighbour V of U
            tempDistance <- distance[U] + edge_weight(U, V)
            if tempDistance < distance[V]
                distance[V] <- tempDistance
                previous[V] <- U
    return distance[], previous[]

Time Complexity: O(V^2)
 


Floyd Warshall


let dist be a |V| × |V| array of minimum distances initialized to ∞ (infinity)
 for each edge (u,v)
    dist[u][v] ← w(u,v)  // the weight of the edge (u,v)
 for each vertex v
    dist[v][v] ← 0
 for k from 1 to |V|
    for i from 1 to |V|
       for j from 1 to |V|
          if dist[i][j] > dist[i][k] + dist[k][j] 
             dist[i][j] ← dist[i][k] + dist[k][j]
         end if
Time Complexity : O(n3) 

Multistage Algorithm

To write a simple algorithm, assign numbers to the vertices so those in stage Vi have lower number those in stage Vi+1.

int[] MStageForward(Graph G)
{
   // returns vector of vertices to follow through the graph
   // let c[i][j] be the cost matrix of G

   int n = G.n (number of nodes);
   int k = G.k (number of stages);
   float[] C = new float[n];
   int[]   D = new int[n];
   int[]   P = new int[k];
   for (i = 1 to n) C[i] = 0.0;
   for j = n-1 to 1 by -1 {
      r = vertex such that (j,r) in G.E and c(j,r)+C(r) is minimum
      C[j] = c(j,r)+C(r);
      D[j] = r;
   }
   P[1] = 1; P[k] = n;
   for j = 2 to k-1 {
      P[j] = D[P[j-1]];
   }
   return P;
}
 Time complexity of Multistage Graph is O(n2) or O(V2

Huffman coding

1. Initialization: Put all nodes in an OPEN list, keep it sorted at all times (e.g., ABCDE).
2. Repeat until the OPEN list has only one node left:
(a) From OPEN pick two nodes having the lowest frequencies/probabilities, create a parent node of them.
(b) Assign the sum of the children's frequencies/probabilities to the parent node and insert it into OPEN.
(c) Assign code 0, 1 to the two branches of the tree, and delete the children from OPEN.
  • HUFFMAN(C)
  • n |C|
  • Q C
  • for i 1 to n - 1
  • do allocate a new node z
  • left[z] x EXTRACT-MIN (Q)
  • right[z] y EXTRACT-MIN (Q)
  • f [z] f [x] + f [y]
  • INSERT(Q, z)
  • return EXTRACT-MIN(Q)
Time Complexity O(nlogn).

Activity Selection problem :

Greedy-Iterative-Activity-Selector(A, s, f):
Sort A by finish times stored in f

S = {A[1]}
k = 1

n = A.length

for i = 2 to n:
if s[i] ≥ f[k]:
S = S U {A[i]}
k = i

return S

The Time complexity of this problem is O(n log n) when the list is not sorted.
When the sorted list is provided the complexity will be O(n).

 

Job Selection problem :

 

Algorithm: Job-Sequencing-With-Deadline (D[1...n], J[1....n], n, p[1...n],list[1...n]) 
  1. initialize list to 0
  2. profit=0
  3. for i to n do:
  4. k = d[i];
  5. while(k>0)
  6. if list[k]=0 then
  7. list[k]=j[i];
  8. profit=profit+p[i]
  9. go to step 3;
  10. end if
  11. decrement k by 1
  12. end while
  13. end for
  14. print list

Time Complexity :O(N^2)


 

0 1 Knapsack problem :

Dynamic-0-1-knapsack (p, w, n, W) 
for w = 0 to W do 
   c[0, w] = 0 
for i = 1 to n do 
   c[i, 0] = 0 
   for i to n 
      for w to W do 
      if w[i] ≤ w then 
         if p[i] + c[i-1, w-w[i]] then 
            c[i, w] = p[i] + c[i-1, w-w[i]]
        else c[i, w] = c[i-1, w] 
     end for 
print c[n][W]

  i=n k=W

while (k>0 & i>0)
    if (c[i][k]<>c[i-1][k])
      print i 
      k = k - w[i]
    end if 
     i = i-1
End while
// Input:
// Values (stored in array p ie profit)
// Weights (stored in array w)
// Number of distinct items (n)
// Knapsack capacity (W)
// NOTE: The array "p" and array "w" are assumed to store all relevant values starting at index 1.

 
 Time complexity of 0 1 Knapsack problem is O(nW) 


Fractional Knapsack problem :


Greedy-Fractional-Knapsack (w[1..n], p[1..n], W)
for i = 1 to n 
   do x[i] = 0 
weight = 0 
for i = 1 to n 
   if weight + w[i] ≤ W then  
      x[i] = 1 
      weight = weight + w[i] 
   else 
      x[i] = (W - weight) / w[i] 
      weight = W 
      break 
return x
 
 Time Complexity O(n logn)
 

Matrix chain :


 
/ Matrix A[i] has dimension dims[i-1] x dims[i] for i = 1..n
MatrixChainOrder(int dims[])
{
    // length[dims] = n + 1
    n = dims.length - 1;
    // m[i,j] = Minimum number of scalar multiplications (i.e., cost)
    // needed to compute the matrix A[i]A[i+1]...A[j] = A[i..j]
    // The cost is zero when multiplying one matrix
    for (i = 1; i <= n; i++)
        m[i, i] = 0;

    for (len = 2; len <= n; len++) { // Subsequence lengths
        for (i = 1; i <= n - len + 1; i++) {
            j = i + len - 1;
            m[i, j] = MAXINT;
            for (k = i; k <= j - 1; k++) {
                cost = m[i, k] + m[k+1, j] + dims[i-1]*dims[k]*dims[j];
                if (cost < m[i, j]) {
                    m[i, j] = cost;
                    s[i, j] = k; // Index of the subsequence split that achieved minimal cost
                }
            }
        }
    }
}
 
 
Time complexity O(n3) 
Space Complexity O(n2) 

karatsuba :

procedure karatsuba(num1, num2)
  if (num1 < 10) or (num2 < 10)
    return num1*num2

  /* calculates the size of the numbers */
  m = min(size_base10(num1), size_base10(num2))
  m2 = floor(m/2) 
  /*m2 = ceil(m/2) will also work */

  /* split the digit sequences in the middle */
  high1, low1 = split_at(num1, m2)
  high2, low2 = split_at(num2, m2)

  /* 3 calls made to numbers approximately half the size */
  z0 = karatsuba(low1, low2)
  z1 = karatsuba((low1 + high1), (low2 + high2))
  z2 = karatsuba(high1, high2)

  return (z2 * 10 ^ (m2 * 2)) + ((z1 - z2 - z0) * 10 ^ m2) + z0
 
  Time Complexity : O(nlog23) ≈ O(n1.585)
   space complexity : O(n) 


Strassen's Multiplication :



p= (A11+A22) (B11+B22) 
q= B11(A21+A22) //BAAB +--+ 
r= A11(B12B22) 
s= A22(B21B11) 
t= B22(A11+A12) 
u= (A21A11)(B11+B12)  //s+t and exchange A & B
v= (A12A22)(B21+B22)  //r+s and exchange A & B

C11=p+s−t+v             
C12=r+t                           //rat
C21=q+s                         //r-1 = q  t-1=s
C22=p−q+r+u

 Time Complexity :O(n^2.80)


Counting  inversion :

def mergeSortInversions(arr):
    if len(arr) == 1:
        return arr, 0
    else:
        a = arr[:len(arr)/2]
        b = arr[len(arr)/2:]
        a, ai = mergeSortInversions(a)
        b, bi = mergeSortInversions(b)
        c = []
        i = 0
        j = 0
        inversions = 0 + ai + bi
    while i < len(a) and j < len(b):
        if a[i] <= b[j]:
            c.append(a[i])
            i += 1
        else:
            c.append(b[j])
            j += 1
            inversions += (len(a)-i)
    c += a[i:]
    c += b[j:]
return c, inversions

Time Complexity : O(n * log n) 
space complexity: o(n) 


 Optimal-Binary-Search-Tree(p, q, n)

e[1…n + 1, 0…n],  
w[1…n + 1, 0…n], 
root[1…n + 1, 0…n]  
for i = 1 to n + 1 do 
   e[i, i - 1] := qi - 1 
   w[i, i - 1] := qi - 1  
for l = 1 to n do 
   for i = 1 to n – l + 1 do 
      j = i + l – 1 e[i, j] := ∞ 
      w[i, i] := w[i, i -1] + pj + qj 
      for r = i to j do 
         t := e[i, r - 1] + e[r + 1, j] + w[i, j] 
         if t < e[i, j] 
            e[i, j] := t 
            root[i, j] := r 
return e and root 
Time Complexity : O(n^3) 


 Longest Common Subsequence

function LCSLength(X[1..m], Y[1..n])
    C = array(0..m, 0..n)
    for i := 0..m
       C[i,0] = 0
    for j := 0..n
       C[0,j] = 0
    for i := 1..m
        for j := 1..n
            if X[i] = Y[j]
                C[i,j] := C[i-1,j-1] + 1
            else
                C[i,j] := max(C[i,j-1], C[i-1,j])

MEMOIZATION
(* Straightforward implementation of fib has exponential number of
 * recursive calls. Requires n non-negative *)
let rec fib n = if n < 2 then 1 else fib (n-1) + fib (n-2)

(* However most of these recursive calls share common subproblems -
 * there are only n distinct subproblems to solve, one for each value
 * of n.  The idea of memoization is to keep track of these results so
 * they can be looked up rather than recomputed. *)
let fibm n =
  let memo : int option array = Array.create (n+1) None in
  let rec f_mem n =
    match memo.(n) with
      Some result -> result       (* computed already! *)
    | None ->
        let result = if n < 2 then 1 else f_mem (n-1) + f_mem (n-2) in
        memo.(n) <- Some result;  (* record in table *)
        result
  in f_mem n




APPROXIMATION ALGORITHMS

vertex cover

APPROXIMATION-VERTEX-COVER(G):
 2 C = 
 3 E'= G.E
 4 
 5 while E'≠ :
 6     let (u, v) be an arbitrary edge of E'
 7     C = C  {u, v}
 8     remove from E' every edge incident on either u or v
 9 
10 return C

Set Cover problem,

we are given a ground set of n elements E = {e1, e2, . . . , en} and a collection of m subsets of E: S := {S1, S2, · · · , Sm} and a nonnegative weight function cost : S → Q+. We will sometimes use wj = cost(Sj ). The goal is to find a minimum weight collection of subsets that covers all elements in E. Formally we want to find a set cover C that minimizes ΣSj∈C wj subject to S Sj∈C Sj = E. If wj = 1 for all j, then the problem is called the unweighted set cover problem. • Set Cover is a problem whose study has led to the development of fundamental techniques for the entire field of approximation algorithms [2]. • It is a generalization of many other important NPC problems such as vertex cover and edge cover. • It is used in the development of antivirus products, VLSI design and many other practical problems.

3.1 Algorithm
1. Initialize C ← φ.
2. While C does not cover all elements in E do
(a) Define cost-effectiveness of each set S ∈ S as αS =cost(S)|S\C|
(b) Find S, the most cost-effective set in the current iteration.
(c) Pick S and for all newly covered elements e ∈ S \ C, set price(e) = αS.
(d) C ← C ∪ S.
3. Output C.


The algorithm for the approximate subset sum problem is as follows:
 initialize a list S to contain one element 0.
 for each i from 1 to N do
   let T be a list consisting of xi + y, for all y in S
   let U be the union of T and S
   sort U
   make S empty 
   let y be the smallest element of U 
   add y to S 
   for each element z of U in increasing order do
      //trim the list by eliminating numbers close to one another
      //and throw out elements greater than s
     if y + cs/N < zs, set y = z and add z to S 
 if S contains a number between (1 − c)s and s, output yes, otherwise no

Comments

Popular posts from this blog

Interview Preparation Kit

Dinosaurus_Island_Character_level_language_model

How to crack the interviews and get a decent job?