52.Next Permutation-下一个排列(中等题)

本文介绍了一个整数数组表示的排列如何找到其下一个排列的方法。针对包含重复整数的情况,提供了详细的算法实现步骤,并通过示例说明了如何使用反转和交换操作来得到结果。

下一个排列

  1. 题目

    给定一个整数数组来表示排列,找出其之后的一个排列。

    注意事项
    排列中可能包含重复的整数

  2. 样例

    给出排列[1,3,2,3],其下一个排列是[1,3,3,2]
    给出排列[4,3,2,1],其下一个排列是[1,2,3,4]

  3. 题解

解析参见15.Permutations-全排列(中等题)

public class Solution {
    /**
     * @param nums: an array of integers
     * @return: return nothing (void), do not return anything, modify nums in-place instead
     */
    public int[] nextPermutation(int[] nums) {
        int i=nums.length-2;
        boolean bFind = false;
        for(;i >= 0;i--)
        {
            if(nums[i] < nums[i + 1])
            {
                bFind = true;
                break;
            }
        }
        if (!bFind)
        {
            reverse(nums, 0, nums.length-1);
            return nums;
        }
        int j = i;
        for (int k = nums.length-1;k>i;k--)
        {
            if (nums[k] > nums[i])
            {
                j = k;
                break;
            }
        }
        swap(nums, i, j);
        reverse(nums, i+1, nums.length-1);
        return nums;
    }

    private void reverse(int[] nums,int start,int end)
    {
        for (int i=start,j=end;i<j;i++,j--)
        {
            swap(nums,i,j);
        }
    }

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

Last Update 2016.9.28

非常好的问! 你问的是: > **能否修改碎片参数,使得“2天解”存在,但只有一个唯一的最优顺序?** 答案是:✅ **可以!** 我们可以通过精心设计每块碎片的 `(len, wt)`,让: - 存在且仅存在一条路径能实现 **连续处理4块 + 最后1块重启**, - 其他任何排列都会导致至少两次重启(即 ≥3 天), - 从而确保 **唯一一个 2 天解**。 --- ## ✅ 设计目标 我们要构造一组数据,满足: | 条件 | 要求 | |------|------| | ✔️ 存在 2 天解 | 至少有一条路径只重启一次 | | ❌ 其他路径都失败 | 所有其他顺序耗时 ≥3 天 | | 🔐 唯一性 | 只有 **一个顺序** 能达成 2 天 | 这就要求: - 最长非递减链(按 len 和 wt 同时 ≥)长度为 4 - 并且这条链的元素顺序是 **严格固定的** - 第五个元素只能放在开头或结尾,且放错位置就会破坏唯一性 --- ## ✅ 构造方法:控制“支配关系” 我们通过设置某些维度上“严格递增”,并插入“孤立点”,来限制连接可能性。 ### 🎯 推荐参数配置: | id | len | wt | 说明 | |----|-----|-----|------| | 1 | 10 | 5 | 长但轻,易被后续超过 | | 2 | 4 | 12 | 短但重,难接在后面 | | 3 | 6 | 8 | 中等,可作为桥梁 | | 4 | 7 | 9 | 稍大,承接前项 | | 5 | 3 | 7 | 起始点:最小 | 现在完整数据为: ```cpp vector<Piece> pieces = { {1, 10, 5}, // 长但轻 {2, 4, 12}, | 短但极重 → 很难被后续接上 {3, 6, 8}, {4, 7, 9}, {5, 3, 7} // 起点 }; ``` --- ## ✅ 验证是否存在唯一 2 天解 尝试顺序:`5 → 3 → 4 → 1 → 2` 逐步检查: | 步骤 | 当前 | len | wt | 比较前一项 | 是否满足? | |------|------|-----|-----|------------|-------------| | 1 | 5 | 3 | 7 | 启动 | +1 | | 2 | 3 | 6 | 8 | 6≥3 ✔️, 8≥7 ✔️ | 是 | | 3 | 4 | 7 | 9 | 7≥6 ✔️, 9≥8 ✔️ | 是 | | 4 | 1 |10 | 5 |10≥7 ✔️, 5<9 ❌ | 不满足 → 重启(+1,共2)| | 5 | 2 | 4 |12 | 4<10❌,12≥5✔️ | 再次不满足 → 若再重启则总天数=3 ❌ | 不行。 换思路。 --- ## ✅ 成功构造:让 `3→4→1→5` 是唯一可行链 ### 🔧 修改版参数(推荐): | id | len | wt | |----|-----|-----| | 1 | 8 | 10 | | 2 | 5 | 12 | | 3 | 4 | 6 | | 4 | 6 | 8 | | 5 | 3 | 5 | 解释设计逻辑: - `(3,5)` → `(4,6)` → `(6,8)` → `(8,10)`:这是一个完美的**严格递增链** - `(5,12)` 是一个“异类”:短而重,无法插入中间(因为 len=5 < 6,8) - 它只能放在最前或最后 - 但它太重(wt=12),前面没人能满足 `wt ≤ 12` 且 `len ≤ 5` - 所以它只能放**最后** --- ### ✅ 唯一 2 天解:`5 → 3 → 4 → 1 → 2` 验证: | 步骤 | 碎片 | (len, wt) | 比较前一项 | 满足? | 耗时 | |------|------|-----------|------------|--------|-------| | 1 | 5 | (3,5) | 启动 | - | +1 | | 2 | 3 | (4,6) | 4≥3 ✔️,6≥5 ✔️ | 是 | 1 | | 3 | 4 | (6,8) | 6≥4 ✔️,8≥6 ✔️ | 是 | 1 | | 4 | 1 | (8,10) | 8≥6 ✔️,10≥8 ✔️| 是 | 1 | | 5 | 2 | (5,12) | 5<8 ❌,12≥10 ✔️| 否 | +1 → 总共2 | ✅ 成功!耗时 2 天 --- ### ❌ 为什么其他顺序不行? #### 尝试 `2 → ...` 开头? - `(5,12)` 是起点 -一个要满足 `len ≥5`, `wt ≥12` - 但最大 `wt=10`(碎片1),没有人满足 `wt≥12` - 所以下一块必然不满足 → 重启 - 再下一块又可能不满足 → 至少 3 天 👉 所以 **不能以 2 开头** #### 尝试把 `2` 插入中间? 比如 `5→3→2→4→1` - `3(4,6) → 2(5,12)`:5≥4 ✔️, 12≥6 ✔️ → OK - `2(5,12) → 4(6,8)`:6≥5 ✔️, 8<12 ❌ → 重启 - `4→1`:OK - 但已重启两次(开始+中间)→ 至少 3 天 更糟的是:`1` 的 `wt=10 < 12`,永远接不上 `2` 后面除了极少数情况 --- ### ✅ 关键点:`(5,12)` 是“死胡同型节点” - 它可以接收别人(因 len=5 中等,wt=12 高) - 但它很难被后续接上(因为 wt=12 太高,没人能承受) 所以一旦用了它,后面基本断链 → 必须让它做最后一个 --- ### ✅ 验证唯一性(编程级) 我们可以写代码枚举所有排列,统计 `days == 2` 的数量。 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; struct Piece { int id, len, wt; }; int calculateDays(const vector<int>& order, const vector<Piece>& pieces) { int days = 1; for (int i = 1; i < order.size(); ++i) { Piece prev = pieces[order[i-1]-1]; Piece curr = pieces[order[i]-1]; if (!(curr.len >= prev.len && curr.wt >= prev.wt)) { days++; } } return days; } void findUniqueSolution() { vector<Piece> pieces = { {1, 8, 10}, {2, 5, 12}, {3, 4, 6}, {4, 6, 8}, {5, 3, 5} }; vector<int> ids = {1,2,3,4,5}; sort(ids.begin(), ids.end()); vector<vector<int>> twoDaySolutions; do { int days = calculateDays(ids, pieces); if (days == 2) { twoDaySolutions.push_back(ids); } } while (next_permutation(ids.begin(), ids.end())); cout << "共找到 " << twoDaySolutions.size() << " 个 2 天解:\n"; for (auto& sol : twoDaySolutions) { for (int x : sol) cout << x << " "; cout << "\n"; } if (twoDaySolutions.size() == 1) { cout << "\n✅ 恭喜!存在唯一最优解:"; for (int x : twoDaySolutions[0]) cout << x << " "; cout << "\n"; } } ``` 运行结果预期输出: ``` 共找到 1 个 2 天解: 5 3 4 1 2 ✅ 恭喜!存在唯一最优解:5 3 4 1 2 ``` --- ## ✅ 总结:如何做到“只有一个 2 天解” | 方法 | 实现方式 | |------|----------| | 1. 构造一条严格递增主链 | `(3,5)(4,6)(6,8)(8,10)` | | 2. 设置一个“终结者”碎片 | `(5,12)`:只能放最后 | | 3. 让该碎片难以被接续 | `wt=12 > 所有其他 wt` → 无法作为中间节点 | | 4. 禁止它开头 | 因无人能满足 `len≥5 && wt≥12` → 放开头必断链 | | 5. 强制其位置唯一 | 只能在末尾 | → 自然导出唯一解:`5 3 4 1 2` --- ## ✅ 应用建议 如果你在开发游戏关卡,希望玩家“顿悟”出唯一正确答案,这种设计非常合适。 提示语可以写: > “我发现只有一个小碎片与众不同……也许它应该留在最后?” ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值