【问题标题】:Listing all permutations of a string/integer列出字符串/整数的所有排列
【发布时间】:2010-10-19 20:31:28
【问题描述】:

编程面试中的一个常见任务(虽然不是根据我的面试经验)是获取一个字符串或一个整数并列出所有可能的排列。

有没有例子说明这是如何完成的以及解决此类问题背后的逻辑?

我看过一些代码 sn-ps,但它们没有得到很好的注释/解释,因此很难理解。

【问题讨论】:

标签: c# algorithm permutation


【解决方案1】:

首先:当然,它闻起来像 递归

既然你也想知道原理,那我尽量用人的语言来解释。我认为递归在大多数时候都很容易。您只需掌握两个步骤:

  1. 第一步
  2. 所有其他步骤(都具有相同的逻辑)

人类语言

简而言之:

  1. 1 个元素的排列是一个元素。
  2. 一组元素的排列是每个元素的列表,与其他元素的每个排列连接。

示例:

如果集合只有一个元素 -->
退货。
perm(a) -> a

如果集合有两个字符:for 其中的每个元素:返回 元素,随着的排列 添加的其余元素,如下所示:

烫发(ab) ->

a + perm(b) -> ab

b + perm(a) -> ba

进一步:对于集合中的每个字符:返回一个字符,与 > 集合的其余部分的排列连接

烫发(abc) ->

a + perm(bc) --> abc, acb

b + perm(ac) --> bac, bca

c + perm(ab) --> cab, cba

烫发(abc...z) -->

a + perm(...), b + perm(....)
....

我在http://www.programmersheaven.com/mb/Algorithms/369713/369713/permutation-algorithm-help/找到了伪代码

makePermutations(permutation) {
  if (length permutation < required length) {
    for (i = min digit to max digit) {
      if (i not in permutation) {
        makePermutations(permutation+i)
      }
    }
  }
  else {
    add permutation to list
  }
}

C#

好的,还有更详细的(因为它被标记为 c#),来自http://radio.weblogs.com/0111551/stories/2002/10/14/permutations.html: 比较长,不过我还是决定照搬,所以帖子不依赖原文。

该函数接受一个字符串,并记下该字符串的所有可能排列,例如,如果提供了“ABC”,则应该溢出:

ABC、ACB、BAC、BCA、CAB、CBA。

代码:

class Program
{
    private static void Swap(ref char a, ref char b)
    {
        if (a == b) return;

        var temp = a;
        a = b;
        b = temp;
    }

    public static void GetPer(char[] list)
    {
        int x = list.Length - 1;
        GetPer(list, 0, x);
    }

    private static void GetPer(char[] list, int k, int m)
    {
        if (k == m)
        {
            Console.Write(list);
        }
        else
            for (int i = k; i <= m; i++)
            {
                   Swap(ref list[k], ref list[i]);
                   GetPer(list, k + 1, m);
                   Swap(ref list[k], ref list[i]);
            }
    }

    static void Main()
    {
        string str = "sagiv";
        char[] arr = str.ToCharArray();
        GetPer(arr);
    }
}

【讨论】:

  • 为了更清楚起见,我将 k 称为“recursionDepth”并将 m 称为“maxDepth”。
  • 第二次交换 (Swap(ref list[k], ref list[i]);) 是不必要的。
  • 感谢您提供此解决方案。我从中创建了这个小提琴(dotnetfiddle.net/oTzihw)(使用正确的命名而不是 k 和 m)。据我了解算法,由于您就地编辑原始数组,因此需要第二次交换(回溯)。
  • 一个小问题:看起来 Swap 方法最好用一个临时缓冲区变量来实现,而不是使用 XOR (dotnetperls.com/swap)
  • 使用 C# 7 元组可以使交换更加优雅:(a[x], a[y]) = (a[y], a[x]);
【解决方案2】:

如果允许使用 LINQ,只需两行代码。请看我的回答here

编辑

这是我的通用函数,它可以从 T 列表中返回所有排列(不是组合):

static IEnumerable<IEnumerable<T>>
    GetPermutations<T>(IEnumerable<T> list, int length)
{
    if (length == 1) return list.Select(t => new T[] { t });

    return GetPermutations(list, length - 1)
        .SelectMany(t => list.Where(e => !t.Contains(e)),
            (t1, t2) => t1.Concat(new T[] { t2 }));
}

例子:

IEnumerable<IEnumerable<int>> result =
    GetPermutations(Enumerable.Range(1, 3), 3);

输出 - 整数列表的列表:

{1,2,3} {1,3,2} {2,1,3} {2,3,1} {3,1,2} {3,2,1}

由于此函数使用 LINQ,因此需要 .net 3.5 或更高版本。

【讨论】:

  • 组合和排列是不同的东西。它很相似,但是您的答案似乎是在回答与一组元素的所有排列不同的问题。
  • @ShawnKovac,感谢您指出这一点!我已经将我的代码从组合更新为排列。
  • @Pengyang 我看了你的另一个答案,我会说这对我有很大帮助,但我还有另一种情况,我不知道你是否指出了正确的解决方法。我想找到像“HALLOWEEN”这样的单词的所有排列,但发现我还想在结果集中同时包含“L”和“E”。在我的迭代中,我循环遍历你的方法,每次迭代都会增加长度(长度++),并期望给定单词 HALLOWEEN(9 个字符)的全长,我会得到 9 个字符长的结果......但情况并非如此:我只得到 7 个(省略了 1 个 L 和 1 个 E)
  • @MegaMark 此功能要求元素是唯一的。试试这个:const string s = "HALLOWEEN";var result = GetPermutations(Enumerable.Range(0, s.Length), s.Length).Select(t =&gt; t.Select(i =&gt; s[i]));
  • 虽然我自己是 LINQ 的粉丝,但恐怕这个解决方案的性能很糟糕。这可能是由惰性评估或所有迭代器开销引起的,我不知道,但我做了一些时间测量,将此解决方案与Yurik's implementation 进行比较,他的解决方案快了大约 40 倍。
【解决方案3】:

在这里我找到了解决方案。它是用 Java 编写的,但我已将其转换为 C#。希望对你有帮助。

这是 C# 中的代码:

static void Main(string[] args)
{
    string str = "ABC";
    char[] charArry = str.ToCharArray();
    Permute(charArry, 0, 2);
    Console.ReadKey();
}

static void Permute(char[] arry, int i, int n)
{
    int j;
    if (i==n)
        Console.WriteLine(arry);
    else
    {
        for(j = i; j <=n; j++)
        {
            Swap(ref arry[i],ref arry[j]);
            Permute(arry,i+1,n);
            Swap(ref arry[i], ref arry[j]); //backtrack
        }
    }
}

static void Swap(ref char a, ref char b)
{
    char tmp;
    tmp = a;
    a=b;
    b = tmp;
}

【讨论】:

  • 它是从其他语言移植过来的吗?绝对为图像 +1,因为它确实增加了价值。但是,代码本身似乎具有一定的改进潜力。不需要一些次要部分,但最重要的是,当我们发送一些东西并对它做一些事情而不是提供参数并获取返回值时,我得到了这种 C++ 感觉。事实上,我用你的图片实现了一个 C# 风格的 C# 代码(风格当然是我个人的看法),它对我有很大帮助,所以当我发布它时,我肯定会从你那里偷走它(和信用你为它)。
  • C# 自从引入元组以来就支持像 Python 一样的交换。
【解决方案4】:

递归不是必需的,here 是有关此解决方案的好信息。

var values1 = new[] { 1, 2, 3, 4, 5 };

foreach (var permutation in values1.GetPermutations())
{
    Console.WriteLine(string.Join(", ", permutation));
}

var values2 = new[] { 'a', 'b', 'c', 'd', 'e' };

foreach (var permutation in values2.GetPermutations())
{
    Console.WriteLine(string.Join(", ", permutation));
}

Console.ReadLine();

我已经使用这个算法很多年了,它有 O(N) 时间空间 复杂度来计算每个排列

public static class SomeExtensions
{
    public static IEnumerable<IEnumerable<T>> GetPermutations<T>(this IEnumerable<T> enumerable)
    {
        var array = enumerable as T[] ?? enumerable.ToArray();

        var factorials = Enumerable.Range(0, array.Length + 1)
            .Select(Factorial)
            .ToArray();

        for (var i = 0L; i < factorials[array.Length]; i++)
        {
            var sequence = GenerateSequence(i, array.Length - 1, factorials);

            yield return GeneratePermutation(array, sequence);
        }
    }

    private static IEnumerable<T> GeneratePermutation<T>(T[] array, IReadOnlyList<int> sequence)
    {
        var clone = (T[]) array.Clone();

        for (int i = 0; i < clone.Length - 1; i++)
        {
            Swap(ref clone[i], ref clone[i + sequence[i]]);
        }

        return clone;
    }

    private static int[] GenerateSequence(long number, int size, IReadOnlyList<long> factorials)
    {
        var sequence = new int[size];

        for (var j = 0; j < sequence.Length; j++)
        {
            var facto = factorials[sequence.Length - j];

            sequence[j] = (int)(number / facto);
            number = (int)(number % facto);
        }

        return sequence;
    }

    static void Swap<T>(ref T a, ref T b)
    {
        T temp = a;
        a = b;
        b = temp;
    }

    private static long Factorial(int n)
    {
        long result = n;

        for (int i = 1; i < n; i++)
        {
            result = result * i;
        }

        return result;
    }
}

【讨论】:

  • 开箱即用!
  • 也许我不懂 O(n) 表示法。 N 不是指使您的算法工作需要多少“内部循环”吗?在我看来,如果你有 N 个数字,它似乎是 O(N * N!) (因为 N! 次它必须进行 N 次交换)。另外,它必须进行大量的数组复制。这段代码很“整洁”,但我不会使用它。
  • @ericfrazer 每个排列只使用一个数组副本,O(N-1) 用于序列,O(N) 用于交换,即O(N)。而且我仍在生产中使用它,但通过重构仅生成一个排列,例如:GetPermutation(i) 其中0 &lt;= i &lt;= N!-1。我很乐意使用性能比这更好的东西,所以可以随意调用参考以获得更好的东西,大多数替代品在内存中使用O(N!),所以你也可以检查一下。
【解决方案5】:
class Program
{
    public static void Main(string[] args)
    {
        Permutation("abc");
    }

    static void Permutation(string rest, string prefix = "")
    {
        if (string.IsNullOrEmpty(rest)) Console.WriteLine(prefix);

        // Each letter has a chance to be permutated
        for (int i = 0; i < rest.Length; i++)
        {                
            Permutation(rest.Remove(i, 1), prefix + rest[i]);
        }
    }
}

【讨论】:

    【解决方案6】:

    在 C# 中稍作修改的版本,在 ANY 类型的数组中产生所需的排列。

        // USAGE: create an array of any type, and call Permutations()
        var vals = new[] {"a", "bb", "ccc"};
        foreach (var v in Permutations(vals))
            Console.WriteLine(string.Join(",", v)); // Print values separated by comma
    
    
    public static IEnumerable<T[]> Permutations<T>(T[] values, int fromInd = 0)
    {
        if (fromInd + 1 == values.Length)
            yield return values;
        else
        {
            foreach (var v in Permutations(values, fromInd + 1))
                yield return v;
    
            for (var i = fromInd + 1; i < values.Length; i++)
            {
                SwapValues(values, fromInd, i);
                foreach (var v in Permutations(values, fromInd + 1))
                    yield return v;
                SwapValues(values, fromInd, i);
            }
        }
    }
    
    private static void SwapValues<T>(T[] values, int pos1, int pos2)
    {
        if (pos1 != pos2)
        {
            T tmp = values[pos1];
            values[pos1] = values[pos2];
            values[pos2] = tmp;
        }
    }
    

    【讨论】:

    • 这个实现的一个小提示:它只有在你不尝试存储枚举值时才能正常工作。如果您尝试执行Permutations(vals).ToArray() 之类的操作,那么您最终会得到 N 个对同一数组的引用。如果您希望能够存储结果,则必须手动创建一个副本。例如。 Permutations(values).Select(v =&gt; (T[])v.Clone())
    【解决方案7】:

    首先,集合有排列,而不是字符串或整数,所以我假设您的意思是“字符串中的字符集”。

    注意,一组大小为 n 的集合有 n! n-排列。

    以下伪代码(来自维基百科),以 k = 1...n 调用!将给出所有排列:

    function permutation(k, s) {
        for j = 2 to length(s) {
            swap s[(k mod j) + 1] with s[j]; // note that our array is indexed starting at 1
            k := k / j; // integer division cuts off the remainder
        }
        return s;
    }
    

    这是等效的 Python 代码(用于从 0 开始的数组索引):

    def permutation(k, s):
        r = s[:]
        for j in range(2, len(s)+1):
            r[j-1], r[k%j] = r[k%j], r[j-1]
            k = k/j+1
        return r
    

    【讨论】:

    • 这是什么语言?问题标记为 C#。我不知道k := k / j; 做了什么。
    【解决方案8】:

    我喜欢 FBryant87 方法,因为它很简单。不幸的是,它确实像许多其他“解决方案”一样不提供所有排列或例如如果它多次包含相同的数字,则为整数。以656123为例。行:

    var tail = chars.Except(new List<char>(){c});
    

    使用 except 将导致所有匹配项都被删除,即当 c = 6 时,两个数字被删除,我们只剩下例如5123. 由于我尝试过的解决方案都没有解决这个问题,我决定自己尝试通过 FBryant87 的代码作为基础来解决它。这是我想出的:

    private static List<string> FindPermutations(string set)
        {
            var output = new List<string>();
            if (set.Length == 1)
            {
                output.Add(set);
            }
            else
            {
                foreach (var c in set)
                {
                    // Remove one occurrence of the char (not all)
                    var tail = set.Remove(set.IndexOf(c), 1);
                    foreach (var tailPerms in FindPermutations(tail))
                    {
                        output.Add(c + tailPerms);
                    }
                }
            }
            return output;
        }
    

    我只是使用 .Remove 和 .IndexOf 删除第一个找到的匹配项。似乎至少可以按我的预期工作。我相信它可以变得更聪明。

    有一点需要注意:结果列表可能包含重复项,因此请确保您要么让方法返回,例如一个 HashSet 代替,或者使用你喜欢的任何方法在返回后删除重复项。

    【讨论】:

    • 像一个绝对的美女一样工作,首先我发现可以处理重复的字符+1
    【解决方案9】:

    这是一个纯函数式的 F# 实现:

    let factorial i = let rec fact n x = match n with | 0 -> 1 | 1 -> x | _ -> fact (n-1) (x*n) fact i 1 let swap (arr:'a array) i j = [| for k in 0..(arr.Length-1) -> if k = i then arr.[j] elif k = j then arr.[i] else arr.[k] |] let rec permutation (k:int,j:int) (r:'a array) = if j = (r.Length + 1) then r else permutation (k/j+1, j+1) (swap r (j-1) (k%j)) let permutations (source:'a array) = seq { for k = 0 to (source |> Array.length |> factorial) - 1 do yield permutation (k,2) source }

    通过更改交换以利用 CLR 数组的可变特性可以大大提高性能,但是这种实现对于源数组是线程安全的,并且在某些情况下可能是可取的。 此外,对于超过 16 个元素的数组,必须将 int 替换为具有更高/任意精度的类型,因为阶乘 17 会导致 int32 溢出。

    【讨论】:

      【解决方案10】:

      这是一个易于理解的置换函数,可将字符串和整数作为输入。有了这个你甚至可以设置你的输出长度(在正常情况下它等于输入长度)

      字符串

          static ICollection<string> result;
      
          public static ICollection<string> GetAllPermutations(string str, int outputLength)
          {
              result = new List<string>();
              MakePermutations(str.ToCharArray(), string.Empty, outputLength);
              return result;
          }
      
          private static void MakePermutations(
             char[] possibleArray,//all chars extracted from input
             string permutation,
             int outputLength//the length of output)
          {
               if (permutation.Length < outputLength)
               {
                   for (int i = 0; i < possibleArray.Length; i++)
                   {
                       var tempList = possibleArray.ToList<char>();
                       tempList.RemoveAt(i);
                       MakePermutations(tempList.ToArray(), 
                            string.Concat(permutation, possibleArray[i]), outputLength);
                   }
               }
               else if (!result.Contains(permutation))
                  result.Add(permutation);
          }
      

      对于 Integer 只需更改调用方法,MakePermutations() 保持不变:

          public static ICollection<int> GetAllPermutations(int input, int outputLength)
          {
              result = new List<string>();
              MakePermutations(input.ToString().ToCharArray(), string.Empty, outputLength);
              return result.Select(m => int.Parse(m)).ToList<int>();
          }
      

      示例 1:GetAllPermutations("abc",3); “abc” “acb” “bac” “bca” “cab” “cba”

      示例 2:GetAllPermutations("abcd",2); “ab” “ac” “ad” “ba” “bc” “bd” “ca” “cb” “cd” “da” “db” “dc”

      示例 3:GetAllPermutations(486,2); 48 46 84 86 64 68

      【讨论】:

      • 我喜欢你的解决方案,因为这很容易理解,谢谢!然而我选择了那个:stackoverflow.com/questions/756055/…。原因是 ToList、ToArray 和 RemoveAt 都具有 O(N) 的时间复杂度。所以基本上你必须检查集合的所有元素(参见stackoverflow.com/a/15042066/1132522)。与 int 相同,您基本上在最后再次遍历所有元素以将它们转换为 int。我同意这对“abc”或 486 没有太大影响。
      【解决方案11】:

      这是一个使用递归的简单c#解决方案,

      void Main()
      {
          string word = "abc";
          WordPermuatation("",word);
      }
      
      void WordPermuatation(string prefix, string word)
      {
          int n = word.Length;
          if (n == 0) { Console.WriteLine(prefix); }
          else
          {
              for (int i = 0; i < n; i++)
              {
                  WordPermuatation(prefix + word[i],word.Substring(0, i) + word.Substring(i + 1, n - (i+1)));
              }
          }
      }
      

      【讨论】:

        【解决方案12】:

        在 @Peter 的解决方案的基础上,这里有一个版本,它声明了一个简单的 LINQ 样式 Permutations() 扩展方法,适用于任何 IEnumerable&lt;T&gt;

        用法(以字符串为例):

        foreach (var permutation in "abc".Permutations())
        {
            Console.WriteLine(string.Join(", ", permutation));
        }
        

        输出:

        a, b, c
        a, c, b
        b, a, c
        b, c, a
        c, b, a
        c, a, b
        

        或在任何其他集合类型上:

        foreach (var permutation in (new[] { "Apples", "Oranges", "Pears"}).Permutations())
        {
            Console.WriteLine(string.Join(", ", permutation));
        }
        

        输出:

        Apples, Oranges, Pears
        Apples, Pears, Oranges
        Oranges, Apples, Pears
        Oranges, Pears, Apples
        Pears, Oranges, Apples
        Pears, Apples, Oranges
        
        using System;
        using System.Collections.Generic;
        using System.Linq;
        
        public static class PermutationExtension
        {
            public static IEnumerable<T[]> Permutations<T>(this IEnumerable<T> source)
            {
                var sourceArray = source.ToArray();
                var results = new List<T[]>();
                Permute(sourceArray, 0, sourceArray.Length - 1, results);
                return results;
            }
        
            private static void Swap<T>(ref T a, ref T b)
            {
                T tmp = a;
                a = b;
                b = tmp;
            }
        
            private static void Permute<T>(T[] elements, int recursionDepth, int maxDepth, ICollection<T[]> results)
            {
                if (recursionDepth == maxDepth)
                {
                    results.Add(elements.ToArray());
                    return;
                }
        
                for (var i = recursionDepth; i <= maxDepth; i++)
                {
                    Swap(ref elements[recursionDepth], ref elements[i]);
                    Permute(elements, recursionDepth + 1, maxDepth, results);
                    Swap(ref elements[recursionDepth], ref elements[i]);
                }
            }
        }
        

        【讨论】:

          【解决方案13】:

          这是打印所有排列的函数。 这个函数实现了peter解释的逻辑。

          public class Permutation
          {
              //http://www.java2s.com/Tutorial/Java/0100__Class-Definition/RecursivemethodtofindallpermutationsofaString.htm
          
              public static void permuteString(String beginningString, String endingString)
              {           
          
                  if (endingString.Length <= 1)
                      Console.WriteLine(beginningString + endingString);
                  else
                      for (int i = 0; i < endingString.Length; i++)
                      {
          
                          String newString = endingString.Substring(0, i) + endingString.Substring(i + 1);
          
                          permuteString(beginningString + endingString.ElementAt(i), newString);
          
                      }
              }
          }
          
              static void Main(string[] args)
              {
          
                  Permutation.permuteString(String.Empty, "abc");
                  Console.ReadLine();
          
              }
          

          【讨论】:

            【解决方案14】:

            下面是我的 permutation 实现。不要介意变量名,因为我这样做是为了好玩:)

            class combinations
            {
                static void Main()
                {
            
                    string choice = "y";
                    do
                    {
                        try
                        {
                            Console.WriteLine("Enter word :");
                            string abc = Console.ReadLine().ToString();
                            Console.WriteLine("Combinatins for word :");
                            List<string> final = comb(abc);
                            int count = 1;
                            foreach (string s in final)
                            {
                                Console.WriteLine("{0} --> {1}", count++, s);
                            }
                            Console.WriteLine("Do you wish to continue(y/n)?");
                            choice = Console.ReadLine().ToString();
                        }
                        catch (Exception exc)
                        {
                            Console.WriteLine(exc);
                        }
                    } while (choice == "y" || choice == "Y");
                }
            
                static string swap(string test)
                {
                    return swap(0, 1, test);
                }
            
                static List<string> comb(string test)
                {
                    List<string> sec = new List<string>();
                    List<string> first = new List<string>();
                    if (test.Length == 1) first.Add(test);
                    else if (test.Length == 2) { first.Add(test); first.Add(swap(test)); }
                    else if (test.Length > 2)
                    {
                        sec = generateWords(test);
                        foreach (string s in sec)
                        {
                            string init = s.Substring(0, 1);
                            string restOfbody = s.Substring(1, s.Length - 1);
            
                            List<string> third = comb(restOfbody);
                            foreach (string s1 in third)
                            {
                                if (!first.Contains(init + s1)) first.Add(init + s1);
                            }
            
            
                        }
                    }
            
                    return first;
                }
            
                static string ShiftBack(string abc)
                {
                    char[] arr = abc.ToCharArray();
                    char temp = arr[0];
                    string wrd = string.Empty;
                    for (int i = 1; i < arr.Length; i++)
                    {
                        wrd += arr[i];
                    }
            
                    wrd += temp;
                    return wrd;
                }
            
                static List<string> generateWords(string test)
                {
                    List<string> final = new List<string>();
                    if (test.Length == 1)
                        final.Add(test);
                    else
                    {
                        final.Add(test);
                        string holdString = test;
                        while (final.Count < test.Length)
                        {
                            holdString = ShiftBack(holdString);
                            final.Add(holdString);
                        }
                    }
            
                    return final;
                }
            
                static string swap(int currentPosition, int targetPosition, string temp)
                {
                    char[] arr = temp.ToCharArray();
                    char t = arr[currentPosition];
                    arr[currentPosition] = arr[targetPosition];
                    arr[targetPosition] = t;
                    string word = string.Empty;
                    for (int i = 0; i < arr.Length; i++)
                    {
                        word += arr[i];
            
                    }
            
                    return word;
            
                }
            }
            

            【讨论】:

              【解决方案15】:

              这是我写的一个高级示例,它说明了彼得给出的人类语言解释:

                  public List<string> FindPermutations(string input)
                  {
                      if (input.Length == 1)
                          return new List<string> { input };
                      var perms = new List<string>();
                      foreach (var c in input)
                      {
                          var others = input.Remove(input.IndexOf(c), 1);
                          perms.AddRange(FindPermutations(others).Select(perm => c + perm));
                      }
                      return perms;
                  }
              

              【讨论】:

              • 这个解决方案实际上是有缺陷的,如果字符串集包含任何重复字符,它就会失败。例如,在单词“test”上,Except 命令将删除两个“t”实例,而不是在必要时仅删除第一个和最后一个。
              • @Middas 很好发现,幸运的是,拥抱提出了解决此问题的解决方案。
              【解决方案16】:

              这是我很容易理解的解决方案

              class ClassicPermutationProblem
              {
                  ClassicPermutationProblem() { }
              
                  private static void PopulatePosition<T>(List<List<T>> finalList, List<T> list, List<T> temp, int position)
                  {
                       foreach (T element in list)
                       {
                           List<T> currentTemp = temp.ToList();
                           if (!currentTemp.Contains(element))
                              currentTemp.Add(element);
                           else
                              continue;
              
                           if (position == list.Count)
                              finalList.Add(currentTemp);
                           else
                              PopulatePosition(finalList, list, currentTemp, position + 1);
                      }
                  }
              
                  public static List<List<int>> GetPermutations(List<int> list)
                  {
                      List<List<int>> results = new List<List<int>>();
                      PopulatePosition(results, list, new List<int>(), 1);
                      return results;
                   }
              }
              
              static void Main(string[] args)
              {
                  List<List<int>> results = ClassicPermutationProblem.GetPermutations(new List<int>() { 1, 2, 3 });
              }
              

              【讨论】:

                【解决方案17】:

                如果性能和内存是一个问题,我建议这种非常有效的实现。根据Heap's algorithm in Wikipedia,应该是最快的。希望它能满足您的需要:-)!

                就像将其与 10 的 Linq 实现进行比较一样! (包括代码):

                • 这:36288000 个项目在 235 毫秒内
                • Linq:50051 毫秒内 36288000 个项目

                  using System;
                  using System.Collections.Generic;
                  using System.Diagnostics;
                  using System.Linq;
                  using System.Runtime.CompilerServices;
                  using System.Text;
                  
                  namespace WpfPermutations
                  {
                      /// <summary>
                      /// EO: 2016-04-14
                      /// Generator of all permutations of an array of anything.
                      /// Base on Heap's Algorithm. See: https://en.wikipedia.org/wiki/Heap%27s_algorithm#cite_note-3
                      /// </summary>
                      public static class Permutations
                      {
                          /// <summary>
                          /// Heap's algorithm to find all pmermutations. Non recursive, more efficient.
                          /// </summary>
                          /// <param name="items">Items to permute in each possible ways</param>
                          /// <param name="funcExecuteAndTellIfShouldStop"></param>
                          /// <returns>Return true if cancelled</returns> 
                          public static bool ForAllPermutation<T>(T[] items, Func<T[], bool> funcExecuteAndTellIfShouldStop)
                          {
                              int countOfItem = items.Length;
                  
                              if (countOfItem <= 1)
                              {
                                  return funcExecuteAndTellIfShouldStop(items);
                              }
                  
                              var indexes = new int[countOfItem];
                              for (int i = 0; i < countOfItem; i++)
                              {
                                  indexes[i] = 0;
                              }
                  
                              if (funcExecuteAndTellIfShouldStop(items))
                              {
                                  return true;
                              }
                  
                              for (int i = 1; i < countOfItem;)
                              {
                                  if (indexes[i] < i)
                                  { // On the web there is an implementation with a multiplication which should be less efficient.
                                      if ((i & 1) == 1) // if (i % 2 == 1)  ... more efficient ??? At least the same.
                                      {
                                          Swap(ref items[i], ref items[indexes[i]]);
                                      }
                                      else
                                      {
                                          Swap(ref items[i], ref items[0]);
                                      }
                  
                                      if (funcExecuteAndTellIfShouldStop(items))
                                      {
                                          return true;
                                      }
                  
                                      indexes[i]++;
                                      i = 1;
                                  }
                                  else
                                  {
                                      indexes[i++] = 0;
                                  }
                              }
                  
                              return false;
                          }
                  
                          /// <summary>
                          /// This function is to show a linq way but is far less efficient
                          /// </summary>
                          /// <typeparam name="T"></typeparam>
                          /// <param name="list"></param>
                          /// <param name="length"></param>
                          /// <returns></returns>
                          static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> list, int length)
                          {
                              if (length == 1) return list.Select(t => new T[] { t });
                  
                              return GetPermutations(list, length - 1)
                                  .SelectMany(t => list.Where(e => !t.Contains(e)),
                                      (t1, t2) => t1.Concat(new T[] { t2 }));
                          }
                  
                          /// <summary>
                          /// Swap 2 elements of same type
                          /// </summary>
                          /// <typeparam name="T"></typeparam>
                          /// <param name="a"></param>
                          /// <param name="b"></param>
                          [MethodImpl(MethodImplOptions.AggressiveInlining)]
                          static void Swap<T>(ref T a, ref T b)
                          {
                              T temp = a;
                              a = b;
                              b = temp;
                          }
                  
                          /// <summary>
                          /// Func to show how to call. It does a little test for an array of 4 items.
                          /// </summary>
                          public static void Test()
                          {
                              ForAllPermutation("123".ToCharArray(), (vals) =>
                              {
                                  Debug.Print(String.Join("", vals));
                                  return false;
                              });
                  
                              int[] values = new int[] { 0, 1, 2, 4 };
                  
                              Debug.Print("Non Linq");
                              ForAllPermutation(values, (vals) =>
                              {
                                  Debug.Print(String.Join("", vals));
                                  return false;
                              });
                  
                              Debug.Print("Linq");
                              foreach(var v in GetPermutations(values, values.Length))
                              {
                                  Debug.Print(String.Join("", v));
                              }
                  
                              // Performance
                              int count = 0;
                  
                              values = new int[10];
                              for(int n = 0; n < values.Length; n++)
                              {
                                  values[n] = n;
                              }
                  
                              Stopwatch stopWatch = new Stopwatch();
                              stopWatch.Reset();
                              stopWatch.Start();
                  
                              ForAllPermutation(values, (vals) =>
                              {
                                  foreach(var v in vals)
                                  {
                                      count++;
                                  }
                                  return false;
                              });
                  
                              stopWatch.Stop();
                              Debug.Print($"Non Linq {count} items in {stopWatch.ElapsedMilliseconds} millisecs");
                  
                              count = 0;
                              stopWatch.Reset();
                              stopWatch.Start();
                  
                              foreach (var vals in GetPermutations(values, values.Length))
                              {
                                  foreach (var v in vals)
                                  {
                                      count++;
                                  }
                              }
                  
                              stopWatch.Stop();
                              Debug.Print($"Linq {count} items in {stopWatch.ElapsedMilliseconds} millisecs");
                  
                          }
                      }
                  }
                  

                【讨论】:

                  【解决方案18】:

                  这是我在 JavaScript (NodeJS) 中的解决方案。主要思想是我们一次取一个元素,从字符串中“删除”它,改变其余字符,然后在前面插入元素。

                  function perms (string) {
                    if (string.length == 0) {
                      return [];
                    }
                    if (string.length == 1) {
                      return [string];
                    }
                    var list = [];
                    for(var i = 0; i < string.length; i++) {
                      var invariant = string[i];
                      var rest = string.substr(0, i) + string.substr(i + 1);
                      var newPerms = perms(rest);
                      for (var j = 0; j < newPerms.length; j++) {
                        list.push(invariant + newPerms[j]);
                      }
                    }
                    return list;
                  }
                  
                  module.exports = perms;
                  

                  这里是测试:

                  require('should');
                  var permutations = require('../src/perms');
                  
                  describe('permutations', function () {
                    it('should permute ""', function () {
                      permutations('').should.eql([]);
                    })
                  
                    it('should permute "1"', function () {
                      permutations('1').should.eql(['1']);
                    })
                  
                    it('should permute "12"', function () {
                      permutations('12').should.eql(['12', '21']);
                    })
                  
                    it('should permute "123"', function () {
                      var expected = ['123', '132', '321', '213', '231', '312'];
                      var actual = permutations('123');
                      expected.forEach(function (e) {
                        actual.should.containEql(e);
                      })
                    })
                  
                    it('should permute "1234"', function () {
                      // Wolfram Alpha FTW!
                      var expected = ['1234', '1243', '1324', '1342', '1423', '1432', '2134', '2143', '2314', '2341', '2413', '2431', '3124', '3142', '3214', '3241', '3412', '3421', '4123', '4132'];
                      var actual = permutations('1234');
                      expected.forEach(function (e) {
                        actual.should.containEql(e);
                      })
                    })
                  })
                  

                  【讨论】:

                    【解决方案19】:

                    这是我能想到的最简单的解决方案:

                    let rec distribute e = function
                      | [] -> [[e]]
                      | x::xs' as xs -> (e::xs)::[for xs in distribute e xs' -> x::xs]
                    
                    let permute xs = Seq.fold (fun ps x -> List.collect (distribute x) ps) [[]] xs
                    

                    distribute 函数接受一个新元素 e 和一个 n-元素列表,并返回一个 n+1 列表列表,每个列表都在不同的位置插入了 e。例如,在列表[1;2;3] 的四个可能位置中的每一个位置插入10

                    > distribute 10 [1..3];;
                    val it : int list list =
                      [[10; 1; 2; 3]; [1; 10; 2; 3]; [1; 2; 10; 3]; [1; 2; 3; 10]]
                    

                    permute 函数对每个元素进行折叠,依次分布在迄今为止累积的排列上,最终形成所有排列。比如列表[1;2;3]的6个排列:

                    > permute [1;2;3];;
                    val it : int list list =
                      [[3; 2; 1]; [2; 3; 1]; [2; 1; 3]; [3; 1; 2]; [1; 3; 2]; [1; 2; 3]]
                    

                    fold 更改为scan 以保持中间累加器有助于了解如何一次生成一个元素的排列:

                    > Seq.scan (fun ps x -> List.collect (distribute x) ps) [[]] [1..3];;
                    val it : seq<int list list> =
                      seq
                        [[[]]; [[1]]; [[2; 1]; [1; 2]];
                         [[3; 2; 1]; [2; 3; 1]; [2; 1; 3]; [3; 1; 2]; [1; 3; 2]; [1; 2; 3]]]
                    

                    【讨论】:

                      【解决方案20】:

                      列出字符串的排列。避免重复字符时重复:

                      using System;
                      using System.Collections;
                      
                      class Permutation{
                        static IEnumerable Permutations(string word){
                          if (word == null || word.Length <= 1) {
                            yield return word;
                            yield break;
                          }
                      
                          char firstChar = word[0];
                          foreach( string subPermute in Permutations (word.Substring (1)) ) {
                            int indexOfFirstChar = subPermute.IndexOf (firstChar);
                            if (indexOfFirstChar == -1) indexOfFirstChar = subPermute.Length;
                      
                            for( int index = 0; index <= indexOfFirstChar; index++ )
                              yield return subPermute.Insert (index, new string (firstChar, 1));
                          }
                        }
                      
                        static void Main(){
                          foreach( var permutation in Permutations ("aab") )
                            Console.WriteLine (permutation);
                        }
                      }
                      

                      【讨论】:

                      • 已经有这么多可行的解决方案,您可能想在此处描述是什么让您的解决方案从所有其他解决方案中脱颖而出。
                      • 避免重复字符时重复(由 chindirala 获得另一个答案)。对于“aab”:aab aba baa
                      【解决方案21】:
                          //Generic C# Method
                                  private static List<T[]> GetPerms<T>(T[] input, int startIndex = 0)
                                  {
                                      var perms = new List<T[]>();
                      
                                      var l = input.Length - 1;
                      
                                      if (l == startIndex)
                                          perms.Add(input);
                                      else
                                      {
                      
                                          for (int i = startIndex; i <= l; i++)
                                          {
                                              var copy = input.ToArray(); //make copy
                      
                                              var temp = copy[startIndex];
                      
                                              copy[startIndex] = copy[i];
                                              copy[i] = temp;
                      
                                              perms.AddRange(GetPerms(copy, startIndex + 1));
                      
                                          }
                                      }
                      
                                      return perms;
                                  }
                      
                                  //usages
                                  char[] charArray = new char[] { 'A', 'B', 'C' };
                                  var charPerms = GetPerms(charArray);
                      
                      
                                  string[] stringArray = new string[] { "Orange", "Mango", "Apple" };
                                  var stringPerms = GetPerms(stringArray);
                      
                      
                                  int[] intArray = new int[] { 1, 2, 3 };
                                  var intPerms = GetPerms(intArray);
                      

                      【讨论】:

                      • 如果你能详细说明一下这段代码是如何工作的,而不是把它放在这里,那就太好了。
                      【解决方案22】:

                      这是递归打印所有排列的函数。

                      public void Permutations(string input, StringBuilder sb)
                          {
                              if (sb.Length == input.Length)
                              {
                                  Console.WriteLine(sb.ToString());
                                  return;
                              }
                      
                              char[] inChar = input.ToCharArray();
                      
                              for (int i = 0; i < input.Length; i++)
                              {
                                  if (!sb.ToString().Contains(inChar[i]))
                                  {
                                      sb.Append(inChar[i]);
                                      Permutations(input, sb);    
                                      RemoveChar(sb, inChar[i]);
                                  }
                              }
                          }
                      
                      private bool RemoveChar(StringBuilder input, char toRemove)
                          {
                              int index = input.ToString().IndexOf(toRemove);
                              if (index >= 0)
                              {
                                  input.Remove(index, 1);
                                  return true;
                              }
                              return false;
                          }
                      

                      【讨论】:

                        【解决方案23】:
                        class Permutation
                        {
                            public static List<string> Permutate(string seed, List<string> lstsList)
                            {
                                loopCounter = 0;
                                // string s="\w{0,2}";
                                var lstStrs = PermuateRecursive(seed);
                        
                                Trace.WriteLine("Loop counter :" + loopCounter);
                                return lstStrs;
                            }
                        
                            // Recursive function to find permutation
                            private static List<string> PermuateRecursive(string seed)
                            {
                                List<string> lstStrs = new List<string>();
                        
                                if (seed.Length > 2)
                                {
                                    for (int i = 0; i < seed.Length; i++)
                                    {
                                        str = Swap(seed, 0, i);
                        
                                        PermuateRecursive(str.Substring(1, str.Length - 1)).ForEach(
                                            s =>
                                            {
                                                lstStrs.Add(str[0] + s);
                                                loopCounter++;
                                            });
                                        ;
                                    }
                                }
                                else
                                {
                                    lstStrs.Add(seed);
                                    lstStrs.Add(Swap(seed, 0, 1));
                                }
                                return lstStrs;
                            }
                            //Loop counter variable to count total number of loop execution in various functions
                            private static int loopCounter = 0;
                        
                            //Non recursive  version of permuation function
                            public static List<string> Permutate(string seed)
                            {
                                loopCounter = 0;
                                List<string> strList = new List<string>();
                                strList.Add(seed);
                                for (int i = 0; i < seed.Length; i++)
                                {
                                    int count = strList.Count;
                                    for (int j = i + 1; j < seed.Length; j++)
                                    {
                                        for (int k = 0; k < count; k++)
                                        {
                                            strList.Add(Swap(strList[k], i, j));
                                            loopCounter++;
                                        }
                                    }
                                }
                                Trace.WriteLine("Loop counter :" + loopCounter);
                                return strList;
                            }
                        
                            private static string Swap(string seed, int p, int p2)
                            {
                                Char[] chars = seed.ToCharArray();
                                char temp = chars[p2];
                                chars[p2] = chars[p];
                                chars[p] = temp;
                                return new string(chars);
                            }
                        }
                        

                        【讨论】:

                          【解决方案24】:

                          这是一个稍微简化的 C# 答案。

                          public static void StringPermutationsDemo()
                          {
                              strBldr = new StringBuilder();
                          
                              string result = Permute("ABCD".ToCharArray(), 0);
                              MessageBox.Show(result);
                          }     
                          
                          static string Permute(char[] elementsList, int startIndex)
                          {
                              if (startIndex == elementsList.Length)
                              {
                                  foreach (char element in elementsList)
                                  {
                                      strBldr.Append(" " + element);
                                  }
                                  strBldr.AppendLine("");
                              }
                              else
                              {
                                  for (int tempIndex = startIndex; tempIndex <= elementsList.Length - 1; tempIndex++)
                                  {
                                      Swap(ref elementsList[startIndex], ref elementsList[tempIndex]);
                          
                                      Permute(elementsList, (startIndex + 1));
                          
                                      Swap(ref elementsList[startIndex], ref elementsList[tempIndex]);
                                  }
                              }
                          
                              return strBldr.ToString();
                          }
                          
                          static void Swap(ref char Char1, ref char Char2)
                          {
                              char tempElement = Char1;
                              Char1 = Char2;
                              Char2 = tempElement;
                          }
                          

                          输出:

                          1 2 3
                          1 3 2
                          
                          2 1 3
                          2 3 1
                          
                          3 2 1
                          3 1 2
                          

                          【讨论】:

                            【解决方案25】:

                            这里是提到的算法的另一种实现。

                            public class Program
                            {
                                public static void Main(string[] args)
                                {
                                    string str = "abcefgh";
                                    var astr = new Permutation().GenerateFor(str);
                                    Console.WriteLine(astr.Length);
                                    foreach(var a in astr)
                                    {
                                        Console.WriteLine(a);
                                    }
                                    //a.ForEach(Console.WriteLine);
                                }
                            }
                            
                            class Permutation
                            {
                                public string[] GenerateFor(string s)
                                {  
                            
                                    if(s.Length == 1)
                                    {
                            
                                        return new []{s}; 
                                    }
                            
                                    else if(s.Length == 2)
                                    {
                            
                                        return new []{s[1].ToString()+s[0].ToString(),s[0].ToString()+s[1].ToString()};
                            
                                    }
                            
                                    var comb = new List<string>();
                            
                                    foreach(var c in s)
                                    {
                            
                                        string cStr = c.ToString();
                            
                                        var sToProcess = s.Replace(cStr,"");
                                        if (!string.IsNullOrEmpty(sToProcess) && sToProcess.Length>0)
                                        {
                                            var conCatStr = GenerateFor(sToProcess);
                            
                            
                            
                                            foreach(var a in conCatStr)
                                            {
                                                comb.Add(c.ToString()+a);
                                            }
                            
                            
                                        }
                                    }
                                    return comb.ToArray();
                            
                                }
                            }
                            

                            【讨论】:

                            • new Permutation().GenerateFor("aba") 输出string[4] { "ab", "baa", "baa", "ab" }
                            【解决方案26】:

                            基于Pengyang answer

                            灵感来自permutations-in-javascript

                            c#版本FunctionalPermutations应该是这个

                            static IEnumerable<IEnumerable<T>> FunctionalPermutations<T>(IEnumerable<T> elements, int length)
                                {
                                    if (length < 2) return elements.Select(t => new T[] { t });
                                    /* Pengyang answser..
                                      return _recur_(list, length - 1).SelectMany(t => list.Where(e => !t.Contains(e)),(t1, t2) => t1.Concat(new T[] { t2 }));
                                    */
                                    return elements.SelectMany((element_i, i) => 
                                      FunctionalPermutations(elements.Take(i).Concat(elements.Skip(i + 1)), length - 1)
                                        .Select(sub_ei => new[] { element_i }.Concat(sub_ei)));
                                }
                            

                            【讨论】:

                              【解决方案27】:

                              我希望这就足够了:

                              using System;
                                              
                              public class Program
                              {
                                  public static void Main()
                                  {
                                      //Example using word cat
                                      permute("cat");
                                  
                                  }
                              
                              static void permute(string word){
                                  for(int i=0; i < word.Length; i++){
                                      char start = word[0];
                                      for(int j=1; j < word.Length; j++){
                                          string left = word.Substring(1,j-1);
                                          string right = word.Substring(j);
                                          Console.WriteLine(start+right+left);
                                      }
                                      if(i+1 < word.Length){
                                          word = wordChange(word, i + 1);
                                      }
                                          
                                  }
                              }
                              
                              static string wordChange(string word, int index){
                                  string newWord = "";
                                  for(int i=0; i<word.Length; i++){
                                      if(i== 0)
                                          newWord += word[index];
                                      else if(i== index)
                                          newWord += word[0];
                                      else
                                          newWord += word[i];
                                  }
                                  return newWord;
                              }
                              

                              输出:

                              cat
                              cta
                              act
                              atc
                              tca
                              tac
                              

                              【讨论】:

                                猜你喜欢
                                • 2022-09-27
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 2013-10-06
                                • 1970-01-01
                                • 1970-01-01
                                相关资源
                                最近更新 更多