LeetCode 31 next-permutation(字典序排列)

本文解析了LeetCode上一道经典题目“下一个排列”的解题思路。通过从右往左遍历序列,寻找第一个满足升序条件的元素对,之后找到右边恰好大于该元素的数并进行交换,最后对交换后的子序列进行排序,从而得到序列的下一个字典序排列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:https://leetcode.com/problems/next-permutation/

题意:找出序列的下一个字典序排列

思路:从右往左找到第一个满足a[i-1]<a[i]的两个数,然后找到a[i-1]右边恰好大于它的数,并和它交换,最后将a[i-1]右边的所有数排序

Next Permutation

代码:
 

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int flag = 0;
        for(int i = nums.size()-1; i>=1; i--){
            if(nums[i-1]<nums[i]){
                flag = 1;
                sort(nums.begin()+i,nums.end());
                int j = upper_bound(nums.begin()+i,nums.end(),nums[i-1])-nums.begin();
                swap(nums[i-1],nums[j]);
                sort(nums.begin()+i,nums.end());
                break;
            }
        }
        if(flag == 0){
            sort(nums.begin(),nums.end());
        }
    }
};

 

<think>嗯,我现在要了解一下next_permutation这个知识点。首先,我得弄清楚它是什么,是哪个编程语言里的函数。好像听说过在C++的STL里有这个函数,对吧?那它的作用是什么呢?可能和排列生成有关?比如给定一个序列,生成它的下一个排列。那什么是“下一个”排列呢?应该是指按照字典序排列的下一个更大的排列吧。比如,如果当前排列是1,2,3,那么下一个排列就是1,3,2,对吗?不过有时候可能没有下一个排列,这时候函数会不会返回false或者什么? 然后,我需要想一下它的使用方法。函数原型是什么样的?可能是bool next_permutation(first, last),接受迭代器作为参数,然后修改容器内的元素到下一个排列,如果存在的话返回true,否则返回false。比如,如果当前已经是最大的排列,比如3,2,1,那么调用之后会变成最小的排列1,2,3,并返回false吗?或者返回false表示没有下一个排列,但此时排列会被重置?可能需要查证一下。 接下来,具体的例子。比如数组初始是1,2,3,使用next_permutation的话,会生成哪些排列?可能的顺序是123,132,213,231312,321,对吗?那每次调用之后,数组会被修改为下一个排列,直到所有排列遍历完毕,函数返回false。这时候数组应该回到初始状态吗?或者说最后一个排列调用之后,函数返回false,数组变成初始状态?或者不会变? 另外,这个函数是否处理重复元素的情况?比如,如果有元素重复的话,比如1,1,2,那么生成的排列会不会有重复?或者函数会自动处理成唯一的排列?比如,1,1,2的下一个排列应该是1,2,1,再下一个是2,1,1,对吗?这时候next_permutation是否能正确处理这些情况,跳过重复的排列? 还有,关于如何自定义比较函数。比如,如果希望按降序生成排列,可能需要使用greater比较器,但这时候next_permutation的行为会不会改变?比如,初始排列是3,2,1,使用默认的less比较器的话,调用next_permutation会生成更大的排列,但如果用greater的话,可能行为不同。可能需要举例说明。 另外,这个函数的时间复杂度是多少?应该是O(n)的,因为每次生成下一个排列需要线性时间。而全排列总共有n!种,所以生成所有排列的时间是O(n!*n)。不过具体实现可能更高效。 再想一个应用场景。比如,需要遍历所有可能的排列来解决问题,比如旅行商问题的暴力解法,或者需要生成所有排列进行测试的情况。使用next_permutation可以方便地生成,而不需要自己实现排列生成算法。 可能需要注意的事项:在调用next_permutation之前,需要确保序列是排序好的,否则可能无法生成所有排列。比如,如果初始序列不是最小的字典序,那么调用next_permutation只能生成当前排列之后的那些排列,而无法遍历全部。所以通常做法是先排序,然后循环调用直到返回false。 例如,如果有一个数组是3,1,2,那么直接调用next_permutation的话,可能不会得到所有排列,因为初始状态不是最小的。所以正确做法是先排序为1,2,3,然后循环调用。 最后,编写一个示例代码可能会帮助理解。比如,在C++中: #include <algorithm> #include <vector> #include <iostream> using namespace std; int main() { vector<int> v = {1, 2, 3}; do { for (auto i : v) cout << i << " "; cout << endl; } while (next_permutation(v.begin(), v.end())); return 0; } 这段代码会输出所有排列,每个排列一行。注意这里用的是do-while循环,保证第一个排列(初始状态)也会被处理。而next_permutation在最后一个排列时返回false,退出循环。 那如果有重复元素的情况呢?比如,v是{1,1,2},那么生成的排列应该是三个:1,1,2;1,2,1;2,1,1。这个时候next_permutation会不会正确处理,不生成重复的排列?比如,初始排列是1,1,2,调用next_permutation得到1,2,1,再次调用得到2,1,1,再调用则返回false,对吗?是的,这时候函数会自动处理重复元素,生成唯一的排列。 总结一下,next_permutation是C++ STL中的一个函数,用于生成序列的下一个字典序排列。它的基本用法是接受两个迭代器作为参数,修改序列到下一个排列,并返回是否成功。通常需要先排序,然后循环调用直到返回false。需要注意处理重复元素的情况,以及正确使用比较函数。</think>`next_permutation` 是 C++ STL 中的一个重要函数,用于生成序列的**下一个字典序排列**。以下是关键知识点与示例: --- ### **核心知识点** 1. **功能** 修改序列为当前排列的**下一个字典序更大的排列**。若已是最大排列,则将其重置为最小排列并返回 `false`。 2. **函数原型** ```cpp bool next_permutation(Iterator first, Iterator last); ``` - 输入:迭代器范围 `[first, last)`。 - 返回值:`true` 表示存在下一个排列,`false` 表示已到最大排列。 3. **字典序规则** - 类似字母表顺序,例如 `1,2,3` 的下一个排列是 `1,3,2`。 - 若当前排列是最大排列(如 `3,2,1`),则循环回到最小排列 `1,2,3`。 4. **时间复杂度** - 单次调用时间复杂度为 $O(N)$,生成所有排列的总时间为 $O(N! \times N)$。 5. **重复元素处理** - 自动跳过重复排列。例如 `{1,1,2}` 仅生成 3 种唯一排列。 6. **自定义排序规则** 可通过自定义比较函数实现降序排列: ```cpp bool next_permutation(first, last, greater<T>()); ``` --- ### **使用步骤** 1. **排序序列** 需先将序列排序为最小字典序,否则无法遍历所有排列。 2. **循环调用** 使用 `do-while` 循环确保包含初始排列: ```cpp sort(v.begin(), v.end()); do { // 处理当前排列 } while (next_permutation(v.begin(), v.end())); ``` --- ### **示例代码** ```cpp #include <algorithm> #include <vector> #include <iostream> using namespace std; int main() { vector<int> v = {1, 2, 3}; // 1. 必须排序 sort(v.begin(), v.end()); // 2. 生成所有排列 do { for (int num : v) cout << num << " "; cout << endl; } while (next_permutation(v.begin(), v.end())); return 0; } ``` **输出**: ``` 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 ``` --- ### **重复元素示例** 输入 `{1,1,2}`: ```cpp vector<int> v = {1, 1, 2}; sort(v.begin(), v.end()); do { for (int num : v) cout << num << " "; cout << endl; } while (next_permutation(v.begin(), v.end())); ``` **输出**: ``` 1 1 2 1 2 1 2 1 1 ``` --- ### **注意事项** - **必须预先排序**:未排序的序列无法遍历所有排列- **返回值意义**:返回 `false` 表示当前已是最末排列,序列被重置为最小排列- **性能**:适合小规模数据(如 $N \leq 10$),大规模数据需避免暴力枚举。 --- ### **应用场景** - 暴力搜索问题(如旅行商问题)。 - 需要遍历所有排列的测试用例生成。 - 组合数学或排列相关的算法题(如 LeetCode排列题目)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值