【问题标题】:Time Complexity of permutation function置换函数的时间复杂度
【发布时间】:2017-05-28 10:05:28
【问题描述】:

给定一组不同的数字,返回所有可能的排列。

例如,[1,2,3] 有以下排列:
[ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]

我的迭代解决方案是:

public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        result.add(new ArrayList<>());
        for(int i=0;i<nums.length;i++)
        {
            List<List<Integer>> temp = new ArrayList<>();
            for(List<Integer> a: result)
            {
                for(int j=0; j<=a.size();j++)
                {
                    a.add(j,nums[i]);
                    List<Integer> current = new ArrayList<>(a);
                    temp.add(current);
                    a.remove(j);
                }
            }
            result = new ArrayList<>(temp);
        }
        return result;
    }

我的递归解决方案是:

public List<List<Integer>> permuteRec(int[] nums) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if (nums == null || nums.length == 0) {
            return result;
        }
        makePermutations(nums, result, 0);
        return result;
    }


void makePermutations(int[] nums, List<List<Integer>> result, int start) {
    if (start >= nums.length) {
        List<Integer> temp = convertArrayToList(nums);
        result.add(temp);
    }
    for (int i = start; i < nums.length; i++) {
        swap(nums, start, i);
        makePermutations(nums, result, start + 1);
        swap(nums, start, i);
    }
}

private ArrayList<Integer> convertArrayToList(int[] num) {
        ArrayList<Integer> item = new ArrayList<Integer>();
        for (int h = 0; h < num.length; h++) {
            item.add(num[h]);
        }
        return item;
    }

根据我的迭代解决方案的时间复杂度(大哦)是:n * n(n+1)/2~ O(n^3)
我无法弄清楚递归解决方案的时间复杂度。
谁能解释两者的复杂性?

【问题讨论】:

  • n 个元素的排列数是 n!,所以一个算法产生所有 n!排列的时间复杂度为 O(n!)。
  • 对于递归和迭代?
  • @OJASJUNEJA 是的。对于这个问题,这是可以想象的最佳运行时间。想象一下,如果你有一个魔法发生器,它每 1 秒吐出一个排列。您仍然需要等待 n! 秒才能完成此生成器,因为存在 n! 排列。
  • 方法无所谓。如果 n 充分大于 k,则 n! > k^n,所以时间复杂度是 O(n!)。少数几个大于 n 的表达式之一!是一个double exponential,比如n^(n^n)。
  • 复杂度为O(n!) == O(n**(n + 1/2)*exp(-n))en.wikipedia.org/wiki/Stirling's_approximation

标签: java algorithm recursion time-complexity


【解决方案1】:

递归解决方案的复杂度为O(n!),因为它由以下等式控制:T(n) = n * T(n-1) + O(1)

迭代解决方案具有三个嵌套循环,因此复杂度为O(n^3)

但是,迭代解决方案不会为除3 之外的任何数字生成正确的排列。

对于n = 3,您可以看到n * (n - 1) * (n-2) = n!。 LHS 是O(n^3)(或者更确切地说是O(n^n),因为这里是n=3),RHS 是O(n!)

对于列表大小的较大值,例如n,您可以有n 嵌套循环,这将提供有效的排列。这种情况下的复杂度将是O(n^n),这比O(n!) 或者更确切地说是n! &lt; n^n 大得多。有一个相当不错的关系叫做Stirling's approximation,它解释了这种关系。

【讨论】:

  • 递归关系为T(n) = n*T(n-1) + O(n)。并且迭代解决方案工作正常。
  • @MoB。迭代解决方案仅适用于大小为 3 的排列。对于其他大小,操作将需要另一个嵌套循环,因此不是通用解决方案。
  • 你为什么这么认为?它工作得很好。在这里试试:jdoodle.com/ia/6lW
  • 为什么三个嵌套循环的复杂度应该是O(n^3)?有时可能是,也可能不是(如本例)。
【解决方案2】:

输出(这是巨大的)在这个问题中很重要,而不是例程的实现。对于n 不同的项目,有n! 排列作为答案返回,因此我们至少有O(n!) 复杂性。

Stirling's approximation的帮助下

 O(n!) = O(n^(1/2+n)/exp(n)) = O(sqrt(n) * (n/e)^n)

我们可以很容易地看到,O(n!) &gt; O(n^c) 代表 any 常量 c,这就是为什么实现本身添加另一个 O(n^3) 并不重要,因为

 O(n!) + O(n^3) = O(n!)

【讨论】:

    【解决方案3】:

    就方法makePermutations被调用的次数而言,确切的时间复杂度是:

    O( 1 + n + n(n-1) + n(n-1)(n-2) + ... )
    

    对于 n = 3:

    O( 1 + 3 + (3*2) + (3*2*1) ) = O(16)
    

    这意味着,对于 n = 3,方法 makePermutations 将被调用 16 次。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-22
      • 2018-10-19
      • 1970-01-01
      • 2020-07-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多