• 2.23
    • 前面那个较小的数。
    • 后面那个刚好大一点的数。
    • 它俩。
    • 转剩下的尾巴。
      • 第一次找转折点时,从-2 开始,因为有与 i+1 的对比,避免越界
      • 只有当 index 不为-1 时才开始交换转折点
      • 最后不管怎样,都反转转折点+1 后的数组
class Solution {
    public void nextPermutation(int[] nums) {
        
        int i=nums.length-2;
        int temp=-1;
        int tempIndex=-1;
        while(i>=0){
            if(nums[i]<nums[i+1]){
                temp = nums[i];
                tempIndex = i;
                break;
            }
            i--;
        }
        if(tempIndex!=-1){
            i = nums.length-1;
            while(i>=0){
                if(nums[i]>temp){
                    swap(nums,i,tempIndex);
                    break;
                }else{
                    i--;
                }
            }
        }
 
        reverse(nums,tempIndex+1,nums.length-1);
    }
    public void swap(int[] nums,int l,int r){
        int temp = nums[l];
            nums[l] = nums[r];
            nums[r] = temp;
    }
    public void reverse(int[] nums,int start,int end){
        while(start<end){
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start++;
            end--;
        }
    }
}

核心逻辑(三步走策略)

为了找到“刚好大一点”的数,我们需要尽量改动靠右边的低位数字

  1. 从后往前找“转折点”: 找到第一对相邻元素,满足 nums[i] < nums[i+1]。此时,i 位置就是我们需要变大的地方。

  2. 找个稍微大点的数来替它:i 往后的数字里,找一个nums[i] 大、但又是其中最小的数,把它俩交换。

  3. 让后面变最小: 交换后,i 后面的部分还是降序的,为了让整体数值最小,我们需要把 i 之后的部分反转(变成升序)。

举个栗子:[1, 5, 8, 4, 7, 6, 5, 3, 1]

  1. 找转折点: 从后往前看,47 小,所以 4 是转折点。

  2. 找替身:4 后面的 [7, 6, 5, 3, 1] 中,比 4 大的最小数是 5

  3. 交换: 数组变成 [1, 5, 8, **5**, 7, 6, **4**, 3, 1]

  4. 反转尾部:5 后面的 [7, 6, 4, 3, 1] 反转,变成 [1, 3, 4, 6, 7]

  5. 结果: [1, 5, 8, 5, 1, 3, 4, 6, 7]

  • 2.5
    • 前面那个较小的数。
    • 后面那个刚好大一点的数。
    • 它俩。
    • 转剩下的尾巴。
class Solution {
    public void nextPermutation(int[] nums) {
        int i = nums.length-2;
 
        while(i>=0&&nums[i]>=nums[i+1]){
            i--;
        }
        if(i>=0){
            int j=nums.length-1;
            while(j>=0&&nums[j]<=nums[i]){
                j--;
            }
            swap(nums,i,j);
        }
        reverse(nums,i+1);
    }
    public void swap(int[] nums,int l,int r){
        int temp = nums[l];
        nums[l] = nums[r];
        nums[r] = temp;
    }
 
    public void reverse(int[] nums,int start){
        int l = start;
        int r = nums.length-1;
        while(l<=r){
            swap(nums,l,r);
            l++;
            r--;
        }
    }
 
}

这道题的解法非常固定,通常被称为 “两遍扫描法”

我们可以把这个寻找过程想象成在翻越一座 “山峰”

💡 核心步骤拆解

假设数组是:[1, 2, 7, 4, 3, 1]

第一步:找“拐点” (Find the Pivot)

从右往左看,数字通常是越来越大的(升序),这就好比你在爬山。 我们要找到 第一个开始下降 的位置。

  • 1 < 3 (升)

  • 3 < 4 (升)

  • 4 < 7 (升)

  • 7 > 2 (降!) 停!

  • 这里的 2 (索引 1) 就是我们要找的“小数”,我们叫它 i

  • 意义i 后面的这一段 [7, 4, 3, 1] 已经是降序了(已经是这一小段能组成的最大排列),没法变更大了,必须动 i 这个位置才能增大。

第二步:找“大数” (Find the Successor)

既然要替换掉 2,得在它后面的那一堆人里,找一个 比 2 大,但是尽可能小 的数。 从右往左扫:

  • 1 < 2 (不行)

  • 3 > 2 (行!)

  • 这里的 3 就是我们要找的“大数”,我们叫它 j

第三步:交换 (Swap)

23 交换。

  • 数组变成:[1, 3, 7, 4, 2, 1]

  • 此时前缀变成了 1, 3...,肯定比原来的 1, 2... 大了。

第四步:反转 (Reverse)

这时候,交换位置后面的那段序列 [7, 4, 2, 1] 依然是降序的(最大状态)。 为了让整个数“只大一点点”,我们需要把后面这段最大状态变成最小状态(升序)。

  • 直接翻转 [7, 4, 2, 1] [1, 2, 4, 7]

  • 最终结果:[1, 3, 1, 2, 4, 7]

class Solution {
    public void nextPermutation(int[] nums) {
        int i = nums.length - 2;
        
        // 1. 从后往前,找到第一个 下降 的数字 (nums[i] < nums[i+1])
        while (i >= 0 && nums[i] >= nums[i + 1]) {
            i--;
        }
 
        // 注意:如果 i < 0,说明整个数组都是降序的(如 3,2,1),
        // 那就没有下一步了,直接跳到最后一步反转即可(变回 1,2,3)。
        
        if (i >= 0) {
            // 2. 如果找到了拐点,再从后往前,找第一个比 nums[i] 大的数字
            int j = nums.length - 1;
            while (j >= 0 && nums[j] <= nums[i]) {
                j--;
            }
            // 3. 交换它俩
            swap(nums, i, j);
        }
 
        // 4. 把 i 后面的所有数字反转(变成升序,即最小值)
        reverse(nums, i + 1);
    }
 
    // 辅助函数:交换
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
 
    // 辅助函数:反转数组(双指针法)
    private void reverse(int[] nums, int start) {
        int left = start, right = nums.length - 1;
        while (left < right) {
            swap(nums, left, right);
            left++;
            right--;
        }
    }
}