全排列:
1.给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
解题思路:就普通递归就可以解决。可以在数组里双双交换,也可以用下面的解法,用一个状态数组来表示当前下标的元素有没有被取过。
class Solution {
List<List<Integer>> list;
public List<List<Integer>> permute(int[] nums) {
list = new ArrayList<List<Integer>>();
help(nums,new boolean[nums.length],new ArrayList<Integer>());
return list;
}
private void help(int[] nums, boolean[] type, ArrayList<Integer> arrayList) {
if(arrayList.size() == nums.length){
list.add(new ArrayList<>(arrayList));
return;
}
for(int i =0 ;i< nums.length ;i++){
if(type[i]) continue;
arrayList.add(nums[i]);
type[i]=true;
help(nums,type,arrayList);
type[i]=false;
arrayList.remove(arrayList.size()-1);
}
}
}
进阶版全排列:
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
解题思路:总体思路还是记忆递归,维护一个状态数组来判断当前下标的元素有没有遍历过。不同的是,因为有重复元素,我们需要加上一个剪枝的逻辑。首先对数组排序,在递归前有一个判断逻辑,当当前元素等于前一个元素,而且前一个元素已经被遍历过了,那就直接continue,进行下一次遍历。
class Solution {
List<List<Integer>> list;
public List<List<Integer>> permute(int[] nums) {
list = new ArrayList<List<Integer>>();
Arrays.sort(nums);
help(nums,new boolean[nums.length],new ArrayList<Integer>());
return list;
}
private void help(int[] nums, boolean[] type, ArrayList<Integer> arrayList) {
if(arrayList.size() == nums.length){
list.add(new ArrayList<>(arrayList));
return;
}
for(int i =0 ;i< nums.length ;i++){
if(type[i]) continue;
if(i>0 && nums[i] == nums[i-1] && type[i-1]) continue;
arrayList.add(nums[i]);
type[i]=true;
help(nums,type,arrayList);
type[i]=false;
arrayList.remove(arrayList.size()-1);
}
}
}
下一个排列:
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
解题思路:我们可以看一个例子[1,2,3,4,9,8,7,6,5],它的下一个排列是:[1,2,3,5,4,6,7,8,9]。规律是原数组是一个前一部分是顺序结构,后一部分是逆序结构,而找到下一个排列的做法是把顺序的最后一个元素和逆序中刚刚好大于它的元素互换,然后把顺序最后一个元素之后的部分数组变成顺序结构。具体代码如下,需要考虑到纯顺序或者纯逆序的特殊情况。
class Solution {
public void nextPermutation(int[] nums) {
int i = nums.length-1;
while(i>0 && nums[i]<=nums[i-1]) i--;
if(i>0){
int j = nums.length-1;
while(j>i-1 && nums[j]<=nums[i-1]) j--;
swap(nums,i-1,j);
}
int k = nums.length-1;
while(i<k){
swap(nums,i,k);
i++;k--;
}
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
第K个排列:
给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
"123"
"132"
"213"
"231"
"312"
"321"
给定 n 和 k,返回第 k 个排列。
说明:
给定 n 的范围是 [1, 9]。
给定 k 的范围是[1, n!]。
示例 1:
输入: n = 3, k = 3
输出: "213"
示例 2:
输入: n = 4, k = 9
输出: "2314"
解题思路:维护一个记忆数组,用于判断当前元素有没有被使用。
class Solution {
public String getPermutation(int n, int k) {
int arr[] = new int[n];
for(int i =0 ;i<n;i++) arr[i]=i+1;
return help(arr,new StringBuffer(),new int[n],0,n,k);
}
private String help(int[] arr, StringBuffer sb,int[] type,int lawer, int n, int k) {
if(lawer == n){
return sb.toString();
}
int curr = mathlen(n-lawer-1);
for(int i =0 ;i<n;i++){
if(type[i] ==1) continue;//当前元素被使用
if(k>curr) {k-=curr;continue;}
sb.append(arr[i]);
type[i] = 1;
help(arr,sb,type,lawer+1,n,k);
}
return sb.toString();
}
private int mathlen(int lawer){
int sum = 1;
for(int i =1;i<=lawer ;i++) sum*=i;
return sum;
}
}