Print all permutation in lexicographic order

前端 未结 9 2042
悲哀的现实
悲哀的现实 2020-11-30 10:04

I want to print all permutation of string in lexicographic order. I wrote this code:

void permute(char *a, int i, int n) {
   if (i == (n-1)) printf(\"\\\"%s         


        
9条回答
  •  清歌不尽
    2020-11-30 10:52

    I am presuming you want a recursive version.

    Here are two solutions.

    Solution 1)

    Since you want lexicographic, all you need to do is pick the next smallest possible when you need to pick. That's it!

    For example, here is a recursive version in python

    def permute(done, remaining):
      if not remaining:
        print done
        return
    
      sorted_rem = sorted(remaining)
      l = len(sorted_rem)
    
      for i in xrange(0, l):
        c = sorted_rem[i]
    
        # Move to c to done portion.
        done.append(c)
        remaining.remove(c)
    
        # Permute the remaining
        permute(done, remaining)
    
        # Put c back.
        remaining.append(c)
        # Remove from done.
        del done[-1]
    
    permute([], [1,2,3,4])
    

    That's it.

    Solution 2)

    While Solution 1 works and is easy to understand, I suspect we might be wasting some time by sorting. This solution is closer to what you have.

    Recursion is basically mathematical induction in disguise, and that way of thinking is really useful in understanding how to write recursive programs.

    For example, assume your permute method always constructs the permutations in lexicographic order.

    Here is a recursive version, with that assumption, please read the comments to understand what is going on.

    // By induction assumption, permute(a, i, n)
    // goes through all the permutations of a[i], ..., a[n-1]
    // in lexicographic order, by modifying a itself.
    void permute(char *a, int i, int n) {
        if (i == (n-1)) {
           printf("%s\n", a);
          return;
        }
    
        int j;
        // We pick the n-i posibilities for the position a+i, then recursively
        // compute the permutations of a[i+1], ..., a[n-1]
        // So first pick the smallest possible for a+i, recurse.
        // Then the next possible for a+i, then recurse etc.
    
        for (j = i; j < n; j++) {
          permute(a, i+1, n);
          // By our induction assumption, at this point, 
          // a[i+1], a[i+2], .., a[n-1]
          // must be the lexicographically the largest possible!
    
          // So now reverse that portion.
          reverse(a+i+1, a+n-1);
    
          // Now we need to pick the lexicographically next element for
          // position a+i. This is nothing but the element which is just
          // larger than the current a+i.
    
          int k = i+1;
          while(k < n && a[i] > a[k]) {
            k++;
          }
    
          if (k >= n) {
            continue;
          }
          // Choose the next value for a+i.
          swap(a+i, a+k);
        }
        // Notice that the portion a[i+1], ..., a[n-1]  is sorted increasing.
        // when the loop exits. Also a[i] will be the largest element.
        // We need to reverse so that a[i], .., a[n-1] is the lexicographically
        // largest permutation to  maintain the induction (recursion) assumption.
        reverse(a+i+1, a+n-1);
    }
    

    Notice the similarity between this, and the iterative version (specified by the others and section below), where you reverse a chunk at the end, and swap two elements.


    btw, the common iterative algorithm for generating permutations in lexicographic order is Narayana Pandita's algorithm, mentioned by other, but not by name.

    See this link: http://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order

    This is what std::next of C++ and a host of other libraries use.

    This algorithm even works when there are repeated elements, and can in fact be used to generate combinations! (Initialize your array with zeroes and ones).

提交回复
热议问题