Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to calculate time complexity of backtracking algorithm?

How to calculate time complexity for these backtracking algorithms and do they have same time complexity? If different how? Kindly explain in detail and thanks for the help.

1. Hamiltonian cycle:          bool hamCycleUtil(bool graph[V][V], int path[], int pos) {             /* base case: If all vertices are included in Hamiltonian Cycle */             if (pos == V) {                 // And if there is an edge from the last included vertex to the                 // first vertex                 if ( graph[ path[pos-1] ][ path[0] ] == 1 )                     return true;                 else                     return false;             }              // Try different vertices as a next candidate in Hamiltonian Cycle.             // We don't try for 0 as we included 0 as starting point in in hamCycle()             for (int v = 1; v < V; v++) {                 /* Check if this vertex can be added to Hamiltonian Cycle */                 if (isSafe(v, graph, path, pos)) {                     path[pos] = v;                      /* recur to construct rest of the path */                     if (hamCycleUtil (graph, path, pos+1) == true)                         return true;                      /* If adding vertex v doesn't lead to a solution, then remove it */                     path[pos] = -1;                 }             }              /* If no vertex can be added to Hamiltonian Cycle constructed so far, then return false */             return false;         }  2. Word break:         a. bool wordBreak(string str) {             int size = str.size();              // Base case             if (size == 0)                 return true;              // Try all prefixes of lengths from 1 to size             for (int i=1; i<=size; i++) {                 // The parameter for dictionaryContains is str.substr(0, i)                 // str.substr(0, i) which is prefix (of input string) of                 // length 'i'. We first check whether current prefix is in                 // dictionary. Then we recursively check for remaining string                 // str.substr(i, size-i) which is suffix of length size-i                 if (dictionaryContains( str.substr(0, i) ) && wordBreak( str.substr(i, size-i) ))                     return true;             }              // If we have tried all prefixes and none of them worked             return false;         }     b. String SegmentString(String input, Set<String> dict) {            if (dict.contains(input)) return input;            int len = input.length();            for (int i = 1; i < len; i++) {                String prefix = input.substring(0, i);                if (dict.contains(prefix)) {                    String suffix = input.substring(i, len);                    String segSuffix = SegmentString(suffix, dict);                    if (segSuffix != null) {                        return prefix + " " + segSuffix;                    }                }            }            return null;       }   3. N Queens:          bool solveNQUtil(int board[N][N], int col) {             /* base case: If all queens are placed then return true */             if (col >= N)                 return true;              /* Consider this column and try placing this queen in all rows one by one */             for (int i = 0; i < N; i++) {                 /* Check if queen can be placed on board[i][col] */                 if ( isSafe(board, i, col) ) {                     /* Place this queen in board[i][col] */                     board[i][col] = 1;                      /* recur to place rest of the queens */                     if ( solveNQUtil(board, col + 1) == true )                         return true;                      /* If placing queen in board[i][col] doesn't lead to a solution then remove queen from board[i][col] */                     board[i][col] = 0; // BACKTRACK                 }             }         } 

I am actually confused a bit, as for Word Break(b) the complexity is O(2n) but for Hamiltonian cycle its different and so does for printing different permutations of the same string and then again for solving n queens problem.

like image 288
da3m0n Avatar asked Nov 18 '13 14:11

da3m0n


People also ask

Does backtracking reduce time complexity?

Backtracking algorithms are usually used to solve hard problems – i.e. such that we don't know whether a significantly more efficient solution exists. Usually the solution space is quite large and uniform and the algorithm can be implemented so that its time complexity is close to the theoretical lower bound.

What is the space complexity of recursive backtracking?

Do you have any recursive calls that need to be stored on the stack? (In your case, yes: since each call decrements k = n - row by 1, we have k0 = n recursive calls, so the space complexity is O(n) .)


1 Answers

In short:

  1. Hamiltonian cycle : O(N!) in the worst case
  2. WordBreak and StringSegment : O(2^N)
  3. NQueens : O(N!)

Note: For WordBreak there is an O(N^2) dynamic programming solution.


More details:

  1. In Hamiltonian cycle, in each recursive call one of the remaining vertices is selected in the worst case. In each recursive call the branch factor decreases by 1. Recursion in this case can be thought of as n nested loops where in each loop the number of iterations decreases by one. Hence the time complexity is given by:

    T(N) = N*(T(N-1) + O(1))
    T(N) = N*(N-1)*(N-2).. = O(N!)

  2. Similarly in NQueens, each time the branching factor decreases by 1 or more, but not much, hence the upper bound of O(N!)

  3. For WordBreak it is more complicated but I can give you an approximate idea. In WordBreak each character of the string has two choices in the worst case, either to be the last letter in the previous word, or to be the first letter of a new word, hence the branching factor is 2. Therefore for both WordBreak & SegmentString T(N) = O(2^N)

like image 122
Vikram Bhat Avatar answered Oct 13 '22 15:10

Vikram Bhat