题目链接:https://leetcode-cn.com/problems/permutations/
思路:
给定已知序列P = A0 A1 A2 ... An
对P按字典排序,得到P的一个最小排列Pmin = A0 A1 A2 ... A(n-1) ,满足Ai > A(i-1) (1 <= i < n)。因为这个算法的结果是没有重复的、排列从小到大排序的,如果不是最小的排列输入进去,那么排出来的只有比它字典序大的排列。
从Pmin开始,找到刚好比Pmin大的一个排列P(min+1),再找到刚好比P(min+1)大的一个排列,如此重复。
Step1:从后向前(即从A(n-1) -> A0),找到第一对为升序的相邻元素,即Ai < A(i+1)。
若找不到这样的Ai,说明已经找到最后一个全排列,可以返回了。
Step2:从后向前,找到第一个比Ai大的数Aj,交换Ai和Aj。
Step3:将排列中A(i+1) A(i+2) … A(n-1)这个序列的数逆序倒置,即A(n-1) ... A(i+2) A(i+1)。因为由前面第Step1、Step2可以得知,A(i+1) <= A(i+2) <= ... <= A(n-1),,为一个降序序列,应将该序列逆序倒置,所得到的新排列才刚刚好是上一个排列的下一个排列。
Step4:重复步骤1-3,直到返回(返回在Step1中)。
这个算法是C++ STL中next_permutation函数的思想。
图示:
上代码:
class Solution {
public List<List<Integer>> permute(int[] nums) {
//保证输入的nums[]是最小的排列,必须从小到大排序一下
Arrays.sort(nums);
List<List<Integer>> lists = new ArrayList<>();
while (true) {
List<Integer> arrayList = new ArrayList<>();
for (int num : nums) {
arrayList.add(num);
}
lists.add(arrayList);
int i, j;
for (i = nums.length - 2; i >= 0; i--) {
if (nums[i] < nums[i + 1]) {
break;
}
}
if (i == -1) {
return lists;
}
for (j = nums.length - 1; j >= 0; j--) {
if (nums[j] > nums[i]) {
break;
}
}
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
for (int left = i + 1, right = nums.length - 1; left < right; left++,right--) {
tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
}
}
}
}