Donald Knuth wrote the article Structured Program for Creating All Topological Sorting Devices . This article was originally studied in 1974. The following quote from the article led me to a better understanding of the problem (in the text, the relation i & j means "i precedes j"):
The natural way to solve this problem is to allow x 1 to be an element that has no predecessors, then remove all relations from x 1 <j and let x 2 be the element & ne; x 1 without predecessors in the system, since it exists, then erase all relations from x 2 <j, etc. It is not difficult to verify that this method will always be successful, provided there is an oriented loop at the input. Moreover, in a sense, this is the only way, since x 1 must be an element without predecessors, and x 2 must be without predecessors when all relations x 1 <j are deleted, etc. This observation naturally leads to an algorithm that finds all solutions to the topological sorting problem; this is a typical example of the βreturn tripβ procedure, where at each stage we consider the subtask from βFind all paths to complete the partial rearrangement of x 1 x 2 ... x k to topological sorting x 1 x 2 ... x n . The general method is in the division into all possible options x k + 1 .
The central problem in backtrack applications is finding a suitable way to organize data so that the sequence through the possible options x k + 1 ; in this case, we need an effective way to detect the set of all elements & ne; {x 1 , ..., x k } that have no predecessors other than x 1 , ..., x k and maintain this knowledge effectively when we move from one subtask to another.
The document contains pseudo-code for an efficient algorithm. The time complexity for each output is O (m + n), where m is the number of input relations, and n is the number of letters. I wrote a C ++ program that implements the algorithm described in the document that supports variables and function names - which takes the letters and relations from your question as input. I hope that no one complains that this answer will be provided to the program due to the language agnostic tag.
#include <iostream> #include <deque> #include <vector> #include <iterator> #include <map> // Define Input static const char input[] = { 'A', 'D', 'G', 'H', 'B', 'C', 'F', 'M', 'N' }; static const char crel[][2] = {{'A', 'B'}, {'B', 'F'}, {'G', 'H'}, {'D', 'F'}, {'M', 'N'}}; static const int n = sizeof(input) / sizeof(char); static const int m = sizeof(crel) / sizeof(*crel); std::map<char, int> count; std::map<char, int> top; std::map<int, char> suc; std::map<int, int> next; std::deque<char> D; std::vector<char> buffer; void alltopsorts(int k) { if (D.empty()) return; char base = D.back(); do { char q = D.back(); D.pop_back(); buffer[k] = q; if (k == (n - 1)) { for (std::vector<char>::const_iterator cit = buffer.begin(); cit != buffer.end(); ++cit) std::cout << (*cit); std::cout << std::endl; } // erase relations beginning with q: int p = top[q]; while (p >= 0) { char j = suc[p]; count[j]--; if (!count[j]) D.push_back(j); p = next[p]; } alltopsorts(k + 1); // retrieve relations beginning with q: p = top[q]; while (p >= 0) { char j = suc[p]; if (!count[j]) D.pop_back(); count[j]++; p = next[p]; } D.push_front(q); } while (D.back() != base); } int main() { // Prepare std::fill_n(std::back_inserter(buffer), n, 0); for (int i = 0; i < n; i++) { count[input[i]] = 0; top[input[i]] = -1; } for (int i = 0; i < m; i++) { suc[i] = crel[i][1]; next[i] = top[crel[i][0]]; top[crel[i][0]] = i; count[crel[i][1]]++; } for (std::map<char, int>::const_iterator cit = count.begin(); cit != count.end(); ++cit) if (!(*cit).second) D.push_back((*cit).first); alltopsorts(0); }