Skip to content

Commit

Permalink
更正错误解法
Browse files Browse the repository at this point in the history
  • Loading branch information
diguage committed Oct 22, 2024
1 parent 4e4e008 commit 69a6573
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 38 deletions.
89 changes: 80 additions & 9 deletions docs/0031-next-permutation.adoc
Original file line number Diff line number Diff line change
@@ -1,28 +1,99 @@
[#0031-next-permutation]
= 31. Next Permutation
= 31. 下一个排列

{leetcode}/problems/next-permutation/[LeetCode - Next Permutation^]
https://leetcode.cn/problems/next-permutation/[LeetCode - 31. 下一个排列 ^]

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).
* 例如,`+arr = [1,2,3]+` ,以下这些都可以视作 `+arr+` 的排列:`+[1,2,3]+``+[1,3,2]+``+[3,1,2]+``+[2,3,1]+`
The replacement must be *http://en.wikipedia.org/wiki/In-place_algorithm[in-place^]* and use only constant extra memory.
整数数组的 *下一个排列* 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 *下一个排列* 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.
* 例如,`+arr = [1,2,3]+` 的下一个排列是 `+[1,3,2]+`
* 类似地,`+arr = [2,3,1]+` 的下一个排列是 `+[3,1,2]+`
* 而 `+arr = [3,2,1]+` 的下一个排列是 `+[1,2,3]+` ,因为 `+[3,2,1]+`
不存在一个字典序更大的排列。
`1,2,3` → `1,3,2`
给你一个整数数组 `+nums+` ,找出 `+nums+` 的下一个排列。

必须 *https://baike.baidu.com/item/%E5%8E%9F%E5%9C%B0%E7%AE%97%E6%B3%95[原地]* 修改,只允许使用额外常数空间。

`3,2,1` → `1,2,3`

*示例 1:*

`1,1,5` → `1,5,1`
....
输入:nums = [1,2,3]
输出:[1,3,2]
....

*示例 2:*

....
输入:nums = [3,2,1]
输出:[1,2,3]
....

*示例 3:*

....
输入:nums = [1,1,5]
输出:[1,5,1]
....


*提示:*

* `+1 <= nums.length <= 100+`
* `+0 <= nums[i] <= 100+`
== 思路分析

可以看成寻找下一个更大的数。这样更容易理解。

标准的 “下一个排列” 算法可以描述为:

. 从后向前 查找第一个 相邻升序 的元素对 `(i,j)`,满足 `A[i] < A[j]`。此时 `[j,end)` 必然是降序
. 在 `[j,end)` 从后向前 查找第一个满足 `A[i] < A[k]` 的 `k`。`A[i]`、`A[k]` 分别就是上文所说的「小数」、「大数」
. 将 `A[i]` 与 `A[k]` 交换
. 可以断定这时 `[j,end)` 必然是降序,逆置 `[j,end)`,使其升序
. 如果在步骤 1 找不到符合的相邻元素对,说明当前 `[begin,end)` 为一个降序顺序,则直接跳到步骤 4

image::images/0031-01.png[{image_attr}]

image::images/0031-02.png[{image_attr}]

image::images/0031-03.png[{image_attr}]

image::images/0031-04.png[{image_attr}]

image::images/0031-05.png[{image_attr}]

image::images/0031-06.png[{image_attr}]

[[src-0031]]
[tabs]
====
一刷::
+
--
[{java_src_attr}]
----
include::{sourcedir}/_0031_NextPermutation.java[tag=answer]
----
--
// 二刷::
// +
// --
// [{java_src_attr}]
// ----
// include::{sourcedir}/_0031_NextPermutation_2.java[tag=answer]
// ----
// --
====


== 参考资料


5 changes: 5 additions & 0 deletions logbook/202406.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,11 @@
|{doc_base_url}/0005-longest-palindromic-substring.adoc[题解]
|✅ 滑动窗口解法
|{counter:codes}
|{leetcode_base_url}/next-permutation/[31. Next Permutation^]
|{doc_base_url}/0031-next-permutation.adoc[题解]
|❌ 一脸懵逼,可以看成寻找下一个更大的数。这样更容易理解。
|===
截止目前,本轮练习一共完成 {codes} 道题。
78 changes: 49 additions & 29 deletions src/main/java/com/diguage/algo/leetcode/_0031_NextPermutation.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,44 +30,64 @@
*/
public class _0031_NextPermutation {
// tag::answer[]
public static void nextPermutation(int[] nums) {
if (nums == null || nums.length < 2) {
public void nextPermutation(int[] nums) {
int len = nums.length;
for (int i = len - 2; i >= 0; i--) {
if (nums[i] < nums[i + 1]) { //找到小数
//重新找一个较大的数,可能是之前的i+1,也可能不是,
for (int j = len - 1; j > i; j--) {
//最差可能就是i+1,最好是尽量靠右的
if (nums[j] > nums[i]) {
swap(nums, i, j);
//i从最小,换为右边较大的数,然后后面开始逆置
reverse(nums, i + 1, len - 1);
return;
}
}

for (int i = nums.length - 1; i > 0; i--) {
if (nums[i - 1] < nums[i]) {
int temp = nums[i - 1];
nums[i - 1] = nums[i];
nums[i] = temp;
return;
}
}
Arrays.sort(nums);
}
}
reverse(nums, 0, len - 1);
}

private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}

//由于nums[i] < nums[i+1],我们可以知道,到满足这个条件的前提,是必须存在递增,
//从后面往前,遍历了很多个才碰到第一个符合条件的,
//那么说明从第一个符合条件开始往后,后面的肯定都是递减,是降序的,所以逆置即可最快重新排序。
private void reverse(int[] nums, int start, int end) {
while (start < end) {
swap(nums, start, end);
start++;
end--;
}
}
// end::answer[]


public static void main(String[] args) {
int[] a1 = new int[]{1, 2, 3};
nextPermutation(a1);
System.out.println(Arrays.toString(a1));
public static void main(String[] args) {
_0031_NextPermutation solution = new _0031_NextPermutation();
int[] a1 = new int[]{1, 2, 3};
solution.nextPermutation(a1);
System.out.println(Arrays.toString(a1));


int[] a2 = new int[]{3, 2, 1};
nextPermutation(a2);
assert !Objects.equals(a2, new int[]{1, 2, 3});
System.out.println(Arrays.toString(a2));
int[] a2 = new int[]{3, 2, 1};
solution.nextPermutation(a2);
assert !Objects.equals(a2, new int[]{1, 2, 3});
System.out.println(Arrays.toString(a2));

int[] a3 = new int[]{1, 1, 5};
nextPermutation(a3);
assert !Objects.equals(a3, new int[]{1, 5, 1});
System.out.println(Arrays.toString(a3));
int[] a3 = new int[]{1, 1, 5};
solution.nextPermutation(a3);
assert !Objects.equals(a3, new int[]{1, 5, 1});
System.out.println(Arrays.toString(a3));

int[] a4 = new int[]{1, 3, 2};
nextPermutation(a4);
assert !Objects.deepEquals(a4, new int[]{2, 1, 3});
System.out.println(Arrays.toString(a4));
}
int[] a4 = new int[]{1, 3, 2};
solution.nextPermutation(a4);
assert !Objects.deepEquals(a4, new int[]{2, 1, 3});
System.out.println(Arrays.toString(a4));
}
}

0 comments on commit 69a6573

Please sign in to comment.