题目
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
- 例如,
arr = [1,2,3],以下这些都可以视作arr的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1]。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
- 例如,
arr = [1,2,3]的下一个排列是[1,3,2]。 - 类似地,
arr = [2,3,1]的下一个排列是[3,1,2]。 - 而
arr = [3,2,1]的下一个排列是[1,2,3],因为[3,2,1]不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。
示例 1:
输入: nums = [1,2,3]
输出: [1,3,2]
题解
方式一:回溯 + 排序
只允许使用常数空间
List<List<Integer>> list = new LinkedList<>();
public void nextPermutation(int[] nums) {
boolean[] used = new boolean[nums.length];
backtrack(nums, used, new ArrayList<>());
Collections.sort(list, (list1, list2) -> {
int size = list1.size();
for (int i = 0; i < size; i++) {
int cmp = list1.get(i).compareTo(list2.get(i));
if (cmp != 0) {
return cmp;
}
}
return 0;
});
List<Integer> target = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
target.add(nums[i]);
}
List<Integer> next = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
if (target.equals(list.get(i))) {
if (i == list.size() - 1) {
next = list.get(0);
} else {
next = list.get(i + 1);
}
}
}
for (int i = 0; i < next.size(); i++) {
nums[i] = next.get(i);
}
}
public void backtrack(int[] nums, boolean[] used, List<Integer> path) {
if (path.size() == nums.length) {
list.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
if (used[i]) {
continue;
}
path.add(nums[i]);
used[i] = true;
backtrack(nums, used, path);
used[i] = false;
path.remove(path.size() - 1);
}
}
方式二:两次遍历
1.从后往前找降序序列:从数组末尾开始,找到第一个满足 nums[i] < nums[i + 1] 的索引 i
2.从后往前找替换元素:从数组末尾开始,找到第一个满足 nums[j] > nums[i] 的索引 j,交换 nums[i] 和 nums[j]
3.反转后缀:将 nums[i + 1] 到数组末尾的元素反转
public void nextPermutation(int[] nums) {
// 从后往前找降序
int i = nums.length - 2;
while (i >= 0 && nums[i] >= nums[i + 1]) {
i--;
}
// 从后往前找第一个比nums[i]大的替换元素
if (i >= 0) {
int j = nums.length - 1;
while (j >= 0 && nums[i] >= nums[j]) {
j--;
}
swap(nums, i, j);
}
// i 后面的数从降序变为升序
reverse(nums, i + 1);
}
public void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
public void reverse(int[] nums, int start) {
int left = start, right = nums.length - 1;
while (left < right) {
swap(nums, left, right);
left++;
right--;
}
}
总结
算法:模拟、回溯、比较器