Permutations

    Example

    For nums = , the permutations are:

    Challenge

    Do it without recursion.

    排列常见的有数字全排列,字符串排列等。

    使用之前 Subsets 的模板,但是在取结果时只能取list.size() == nums.size()的解,且在添加list元素的时候需要注意除重以满足全排列的要求。此题假设前提为输入数据中无重复元素。

    Python

    1. class Solution:
    2. """
    3. @param nums: A list of Integers.
    4. @return: A list of permutations.
    5. """
    6. def permute(self, nums):
    7. alist = []
    8. result = [];
    9. if not nums:
    10. return result
    11. self.helper(nums, alist, result)
    12. return result
    13. def helper(self, nums, alist, ret):
    14. if len(alist) == len(nums):
    15. # new object
    16. ret.append([] + alist)
    17. return
    18. for i, item in enumerate(nums):
    19. if item not in alist:
    20. alist.append(item)
    21. self.helper(nums, alist, ret)
    22. alist.pop()

    C++

    1. class Solution {
    2. public:
    3. /**
    4. * @param nums: A list of integers.
    5. * @return: A list of permutations.
    6. */
    7. vector<vector<int> > permute(vector<int> nums) {
    8. vector<vector<int> > result;
    9. if (nums.empty()) {
    10. return result;
    11. }
    12. vector<int> list;
    13. backTrack(result, list, nums);
    14. return result;
    15. }
    16. private:
    17. void backTrack(vector<vector<int> > &result, vector<int> &list, \
    18. vector<int> &nums) {
    19. if (list.size() == nums.size()) {
    20. result.push_back(list);
    21. return;
    22. }
    23. for (int i = 0; i != nums.size(); ++i) {
    24. // remove the element belongs to list
    25. if (find(list.begin(), list.end(), nums[i]) != list.end()) {
    26. continue;
    27. }
    28. list.push_back(nums[i]);
    29. backTrack(result, list, nums);
    30. list.pop_back();
    31. }
    32. }
    33. };

    Java

    源码分析

    list.size() == nums.size()时,已经找到需要的解,及时return避免后面不必要的for循环调用开销。

    使用回溯法解题的关键在于如何确定正确解及排除不符条件的解(剪枝)

    以状态数来分析,最终全排列个数应为 n!, 每个节点被遍历的次数为 (n-1)!, 故节点共被遍历的状态数为 O(n!), 此为时间复杂度的下界,因为这里只算了合法条件下的遍历状态数。若不对 list 中是否包含 nums[i] 进行检查,则总的状态数应为 n^n 种。

    由于最终的排列结果中每个列表的长度都为 n, 各列表的相同元素并不共享,故时间复杂度的下界为 O(n \cdot n!), 上界为 n \cdot n^n. 实测helper中 for 循环的遍历次数在 O(2n \cdot n!) 以下,注意这里的时间复杂度并不考虑查找列表里是否包含重复元素。

    Python

    1. class Solution:
    2. # @param {integer[]} nums
    3. # @return {integer[][]}
    4. def permute(self, nums):
    5. if nums is None:
    6. return [[]]
    7. elif len(nums) <= 1:
    8. return [nums]
    9. result = []
    10. for p in self.permute(nums[:i] + nums[i + 1:]):
    11. result.append(p + [item])
    12. return result
    13. class Solution2:
    14. # 类似 subset的模版
    15. def permute(self, nums):
    16. if not nums:
    17. return []
    18. self.helper(sorted(nums), res, [])
    19. return res
    20. def helper(self, nums, res, tmp):
    21. if not nums:
    22. res.append(tmp[:])
    23. return
    24. for i, num in enumerate(nums, 1):
    25. self.helper(nums[:i] + nums[i + 1:], res, tmp + [num])

    C++

    1. class Solution {
    2. public:
    3. /**
    4. * @param nums: A list of integers.
    5. * @return: A list of permutations.
    6. */
    7. vector<vector<int> > permute(vector<int>& nums) {
    8. vector<vector<int> > result;
    9. if (nums.size() == 1) {
    10. result.push_back(nums);
    11. return result;
    12. }
    13. for (int i = 0; i < nums.size(); ++i) {
    14. vector<int> nums_new = nums;
    15. nums_new.erase(nums_new.begin() + i);
    16. vector<vector<int> > res_tmp = permute(nums_new);
    17. for (int j = 0; j < res_tmp.size(); ++j) {
    18. vector<int> temp = res_tmp[j];
    19. temp.push_back(nums[i]);
    20. result.push_back(temp);
    21. }
    22. }
    23. return result;
    24. }
    25. };

    Java

    源码分析

    Python 中使用len()时需要防止None, 递归终止条件为数组中仅剩一个元素或者为空,否则遍历nums数组,取出第i个元素并将其加入至最终结果。nums[:i] + nums[i + 1:]即为去掉第i个元素后的新列表。

    Java 中 ArrayList 和 List 的类型转换需要特别注意。

    由于取的结果都是最终结果,无需去重判断,故时间复杂度为 O(n!), 但是由于nums[:i] + nums[i + 1:]会产生新的列表,实际运行会比第一种方法慢不少。

    递归版的程序比较简单,咱们来个迭代的实现。非递归版的实现也有好几种,这里基于 C++ STL 中next_permutation的字典序实现方法。参考 Wikipedia 上的字典序算法,大致步骤如下:

    1. 从后往前寻找索引满足 a[k] < a[k + 1], 如果此条件不满足,则说明已遍历到最后一个。
    2. 从后往前遍历,找到第一个比a[k]大的数a[l], 即a[k] < a[l].
    3. 交换a[k]a[l].
    4. 反转k + 1 ~ n之间的元素。

    Python

    1. class Solution:
    2. # @param {integer[]} nums
    3. # @return {integer[][]}
    4. def permute(self, nums):
    5. if nums is None:
    6. return [[]]
    7. elif len(nums) <= 1:
    8. return [nums]
    9. # sort nums first
    10. nums.sort()
    11. result = []
    12. while True:
    13. result.append([] + nums)
    14. # step1: find nums[i] < nums[i + 1], Loop backwards
    15. i = 0
    16. for i in xrange(len(nums) - 2, -1, -1):
    17. if nums[i] < nums[i + 1]:
    18. break
    19. elif i == 0:
    20. return result
    21. # step2: find nums[i] < nums[j], Loop backwards
    22. j = 0
    23. for j in xrange(len(nums) - 1, i, -1):
    24. if nums[i] < nums[j]:
    25. break
    26. # step3: swap betwenn nums[i] and nums[j]
    27. nums[i], nums[j] = nums[j], nums[i]
    28. # step4: reverse between [i + 1, n - 1]
    29. nums[i + 1:len(nums)] = nums[len(nums) - 1:i:-1]
    30. return result

    C++

    1. class Solution {
    2. public:
    3. /**
    4. * @param nums: A list of integers.
    5. * @return: A list of permutations.
    6. */
    7. vector<vector<int> > permute(vector<int>& nums) {
    8. vector<vector<int> > result;
    9. if (nums.empty() || nums.size() <= 1) {
    10. return result;
    11. }
    12. sort(nums.begin(), nums.end());
    13. for (;;) {
    14. result.push_back(nums);
    15. // step1: find nums[i] < nums[i + 1]
    16. int i = 0;
    17. for (i = nums.size() - 2; i >= 0; --i) {
    18. if (nums[i] < nums[i + 1]) {
    19. break;
    20. } else if (0 == i) {
    21. return result;
    22. }
    23. }
    24. // step2: find nums[i] < nums[j]
    25. int j = 0;
    26. for (j = nums.size() - 1; j > i; --j) {
    27. if (nums[i] < nums[j]) break;
    28. }
    29. // step3: swap betwenn nums[i] and nums[j]
    30. int temp = nums[j];
    31. nums[j] = nums[i];
    32. nums[i] = temp;
    33. // step4: reverse between [i + 1, n - 1]
    34. reverse(nums, i + 1, nums.size() - 1);
    35. }
    36. return result;
    37. }
    38. private:
    39. void reverse(vector<int>& nums, int start, int end) {
    40. for (int i = start, j = end; i < j; ++i, --j) {
    41. int temp = nums[i];
    42. nums[i] = nums[j];
    43. nums[j] = temp;
    44. }
    45. }
    46. };

    Java - Array

    Java - List

    1. class Solution {
    2. /**
    3. * @param nums: A list of integers.
    4. * @return: A list of permutations.
    5. */
    6. public ArrayList<ArrayList<Integer>> permute(ArrayList<Integer> nums) {
    7. ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
    8. if (nums == null || nums.size() == 0) return result;
    9. // deep copy(do not change nums)
    10. List<Integer> perm = new ArrayList<Integer>(nums);
    11. // sort first!!!
    12. Collections.sort(perm);
    13. while (true) {
    14. // step0: add perm into result
    15. result.add(new ArrayList<Integer>(perm));
    16. // step1: search the first num[k] < num[k+1] backward
    17. int k = -1;
    18. for (int i = perm.size() - 2; i >= 0; i--) {
    19. if (perm.get(i) < perm.get(i + 1)) {
    20. k = i;
    21. break;
    22. }
    23. }
    24. // if current rank is the largest, exit while loop
    25. if (k == -1) break;
    26. // step2: search the first perm[k] < perm[l] backward
    27. int l = perm.size() - 1;
    28. while (l > k && perm.get(l) <= perm.get(k)) l--;
    29. // step3: swap perm[k] with perm[l]
    30. Collections.swap(perm, k, l);
    31. // step4: reverse between k+1 and perm.size()-1;
    32. reverse(perm, k + 1, perm.size() - 1);
    33. }
    34. return result;
    35. }
    36. private void reverse(List<Integer> nums, int lb, int ub) {
    37. for (int i = lb, j = ub; i < j; i++, j--) {
    38. Collections.swap(nums, i, j);
    39. }
    40. }
    41. }

    复杂度分析

    除了将 n! 个元素添加至最终结果外,首先对元素排序,时间复杂度近似为 O(n \log n), 反转操作近似为 O(n), 故总的时间复杂度为 O(n!). 除了保存结果的外,其他空间可忽略不计,所以此题用生成器来实现较为高效,扩展题可见底下的 Python itertools 中的实现,从 n 个元素中选出 m 个进行全排列。