Permutation类型题目整理

1.permutations

Given a list of numbers, return all possible permutations.

For nums = [1,2,3], the permutations are:

[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

显然需要需要用递归来找所有的全排列。关于这个递归,是返回找到的值加入到上层还是在把参数传到递归层,在递归层进行更新处理。对于分治策略,二叉树的问题,很明显左右子树能得到一个小问题的答案,大问题的答案是该小问题的答案的一个组合,那么用递归返回一个结果,在上层进行结合处理比较方便。对于这个题目,每次递归要做的事情是往下找permutations的下一个数字,在递归层处理结果更加自然。

注意深刻理解这个递归要干的事情,每一层分别是做什么的。

for (int i = 0; i < nums.length; i++) {
  。。。
  list.add(nums[i]);

  helper(nums, result, list);
  list.remove(list.size() - 1);

  。。。

}

这个for循环其实是在便利nums数组,找到第一个合适的permutation的首字母,这也就是为什么找到第一个首字母的时候,我们要把它更新的结果里面。这时候需要找第二个字母,那么我们跳到第二层递归里面去找第二个,这也就是为什么,这个时候需要使用递归。当以当前首字母开头的permutations找到并且加入到全局结果里以后,我们需要找下一个合适的开头字母,所以要把当前的开头字母给清空,这也就是为什么需要做remove操作。

 1 class Solution {
 2     /**
 3      * @param nums: A list of integers.
 4      * @return: A list of permutations.
 5      */
 6     public List<List<Integer>> permute(int[] nums) {
 7         List<List<Integer>> result = new ArrayList<List<Integer>>();
 8         if (nums == null) {
 9             return result;
10         }
11         List<Integer> list = new ArrayList<Integer>();
12         helper(nums,result,list);
13         return result;
14     }
15     private void helper (int[] nums, List<List<Integer>> result, List<Integer> list) {
16         if (list.size() == nums.length) {
17             result.add(new ArrayList<Integer>(list));
18         }
19         for (int i = 0; i < nums.length; i++) {
20             if (list.contains(nums[i])){
21                 continue;
22             }
23             list.add(nums[i]);
24             helper(nums, result, list);
25             list.remove(list.size() - 1);
26         }
27     }
28 }
permutations

2.  Permutations II

Given a list of numbers with duplicate number in it. Find all unique permutations.

Example

For numbers [1,2,2] the unique permutations are:

[
  [1,2,2],
  [2,1,2],
  [2,2,1]
]

注意3点, 1. 对于有重复的情况,用visited[]数组处理,判断这个数是否已经遍历过了

     2. 需要对数组进行排序,如果num是int[] 那么用Arrays.sort

                如果是ArrayList<Integer> 用Collections.sort

     3.对于重复的情况,112, 11第一次出现时是valid, 但是当把第二个1作为开头,第一个1作为第二时是invalid,所以添加了下面的判断条件:

      (i != 0 && nums.get(i) == nums.get(i - 1) && visited[i - 1] == 0)

     针对这个情况,下面是反例:

wrong answer:

Input:[1,1,2]
Output:[[1,1,2],[1,2,1],[1,1,2],[1,2,1],[2,1,1],[2,1,1]]
Expected:[[1,1,2],[1,2,1],[2,1,1]]

 

 1 public class Solution {
 2     public List<List<Integer>> permuteUnique(int[] nums) {
 3         List<List<Integer>> result = new ArrayList<List<Integer>>();
 4         if (nums == null || nums.length == 0) {
 5             return result;
 6         }
 7         List<Integer> list = new ArrayList<Integer>();
 8         int[] visited = new int[nums.length];
 9         Arrays.sort(nums);
10         helper(nums, result, list, visited);
11         return result;
12     }
13     private static void helper(int[] nums, List<List<Integer>> result, List<Integer> list, int[] visited) {
14         if (list.size() == nums.length) {
15             result.add(new ArrayList(list));
16             return;
17         }
18         
19         for (int i = 0; i < nums.length; i++) {
20             if (visited[i] == 1 || (i != 0 && nums[i] == nums[i - 1] && visited[i - 1] == 0)) {
21                 continue;
22             }
23             visited[i] = 1;
24             list.add(nums[i]);
25             helper(nums, result, list, visited);
26             list.remove(list.size() - 1);
27             visited[i] = 0;
28         }
29     }
30 }
permuteUnique

3.Next Permutation

Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.
If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).
The replacement must be in-place, do not allocate extra memory.
Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
 
数学题,没啥可说的 = = 
 1 public class Solution {
 2     /**
 3      * @param nums: an array of integers
 4      * @return: return nothing (void), do not return anything, modify nums in-place instead
 5      */
 6     public int[] nextPermutation(int[] nums) {
 7         if (nums == null || nums.length == 0) {
 8             return nums;
 9         }
10         int size = nums.length;
11         int violation = -1;
12         // search for first violation
13         for (int i = size - 1; i > 0; i--) {
14             if (nums[i - 1] < nums[i]) {
15                 violation = i - 1;
16                 break;
17             }
18         }
19         
20         // if can not find violation, we need to sawp all the array
21         if (violation == -1) {
22             int i = 0, j = size - 1;
23             while(i < j) {
24             swap(nums, i++, j--);
25             }
26             return nums;
27         }
28         
29         
30         // search swap index
31         int index = -1;
32         for (int i = size - 1; i > violation; i--) {
33             if (nums[i] > nums[violation]) {
34                 index = i;
35                 break;
36             }
37         }
38         //swap violation and index
39         swap(nums, violation, index);
40         
41         //swap the tail part after the violation
42         int i = violation + 1, j = size - 1;
43         while(i < j) {
44             swap(nums, i++, j--);
45         }
46         return nums;
47     }
48     private static void swap (int[] nums, int i, int j) {
49         int temp = nums[i];
50         nums[i] = nums[j];
51         nums[j] = temp;
52         return;
53     }
54 }
nextPermutation

 4.  Permutation Sequence

 这个题目的核心思想是确定首位的数字, 比如n = 4 k = 17
  固定第一个位置,第二三四个位置分别是c3*c2*c1 = 6  ----->也就是说1开头的能有6个 而开头的能有6个 3开头的能有6个 所以17个的话是index为2的数,也就是3。
另外代码里面对k做了k - 1处理,比如说第18个序列,也是3开头的最后一个 做除法和余时, 18/3 = 3 那就跳到第4个数去了。注意这个地方返回的是index,这个减一用的很巧妙!
c++的代码相对比较好理解:
 1 class Solution {
 2 public:
 3     string getPermutation(int n, int k) {
 4         string str = string("123456789").substr(0, n);
 5         string res(n, ' ');
 6         for(int i = 0; i < n; i++)
 7             res[i] = helper(str, k);
 8         return res;
 9     }
10     //以s中字符构造的全排列中,返回第k个排列的第一个字符,并且删除s中该字符
11     //s中字符递增有序
12     char helper(string &s, int &k)
13     {
14         int tmp = factorial(s.size()-1), i = (k-1)/tmp;
15         char res = s[i];
16         s.erase(i, 1);
17         k -= i*tmp;//更新k
18         return res;
19     }
20     //求正整数n的阶乘
21     int factorial(int n)
22     {
23         int res = 1;
24         for(int i = 2; i <= n; i++)
25             res *= i;
26         return res;
27     }
28 };
C++ permutation sequence

java的代码相对复杂一点,方法是一样的,达到目的的实现有点绕

 1 public class Solution {
 2 
 3     public String getPermutation(int n, int k) {
 4         StringBuilder sb = new StringBuilder();
 5         boolean[] used = new boolean[n];
 6 
 7         k = k - 1;
 8         int factor = 1;
 9         for (int i = 1; i < n; i++) {
10             factor *= i;
11         }
12 
13         for (int i = 0; i < n; i++) {
14             int index = k / factor;
15             k = k % factor;
16             for (int j = 0; j < n; j++) {
17                 if (used[j] == false) {
18                     if (index == 0) {
19                         used[j] = true;
20                         sb.append((char) ('0' + j + 1));
21                         break;
22                     } else {
23                         index--;
24                     }
25                 }
26             }
27             if (i < n - 1) {
28                 factor = factor / (n - 1 - i);
29             }
30         }
31 
32         return sb.toString();
33     }
34 }
View Code

 

 

posted @ 2016-09-28 09:33  毛线刷题笔记  阅读(1776)  评论(0编辑  收藏  举报