实现C++STL中全排列函数next_permutation

next_permutation函数的使用

对于next_permutation函数,其函数原型为:

#include <algorithm>
bool next_permutation(iterator start,iterator end); 

next_permutation函数将按字母表顺序生成给定序列的下一个较大的序列,若此时序列为降序, 则返回一个最小的序列。

直接看使用效果

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

auto main() -> int
{
    vector<int> v = {1, 2, 3}; 
    vector<int> v2 = {3, 2, 1}; 
    string s = "132"; 
    next_permutation(v.begin(), v.end()); 
    next_permutation(v2.begin(), v2.end()); 
    next_permutation(s.begin(), s.end()); 
    
    for(auto val : v)  cout << val; cout << endl; 
    for(auto val : v2) cout << val; cout << endl; 
    cout << s << endl; 
    return 0;   
}
输出:
132
123
213       

来看力扣上面一道相关的题目 : Leetcode 31.下一个排列

题目描述

实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。

如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

必须原地修改,只允许使用额外常数空间。

样例
示例 1:
输入:nums = [1,2,3]
输出:[1,3,2]

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

示例 3:
输入:nums = [1,1,5]
输出:[1,5,1]

示例 4:
输入:nums = [1]
输出:[1]
数据范围

1 ⩽ \leqslant nums.length ⩽ \leqslant 100
0 ⩽ \leqslant nums[i] ⩽ \leqslant 100


算法1

(库函数next_permutation解法)

不讲码德的写法,面试官肯定不想看到这样的答案

C++ 代码
class Solution 
{
public:
    void nextPermutation(vector<int>& nums) 
    {
        next_permutation(nums.begin(), nums.end()); 
    }
};

算法2

(手动实现next_permutation)
思路分析

由于我们要找到的当前排列的下一个序列, 即我们找到的下一个序列应该只是比当前序列大一点点, 即序列前半部分应该保持不变,而应该把后面部分的变大一点点. 考虑一个这个情况 5 4 3 2 1 , 即一个降序的序列,显然对于一个降序序列我们已经不能将其变得更大. 故我们分析我们当前序列的时候也是同样从序列的最后面开始分析找到第一个非降序的位置。 因为降序的序列我们并不能将其变得更大. 下面以 1 4 5 6 3 2 这个例子具体分析下 :

在这里插入图片描述
简单概括一下上面的找到下一个排列的过程就是,先从后往前找到第一对递增序列, 例如找到的是 a k a_k ak < a k + 1 a_{k + 1} ak+1, 那么我们就知道 a k + 1 a_{k + 1} ak+1 a n a_n an是一个非递增序列(因为题目的序列可能存在重复元素,上面说的降序序其实不太严谨), 故我们应该找到 a k + 1 a_{k + 1} ak+1 a n a_n an中大于 a k a_k ak的最小元素,然后交换这两个元素的位置,然后为了保证当前的序列是大于当前序列的最小序列,我们还需将 a k + 1 a_{k + 1} ak+1 a n a_n an逆序,将其变为非递减序列。 故下面看具体代码实现上面描述的过程。

C++ 代码
class Solution {
public:
    void nextPermutation(vector<int>& nums) 
    {
        int n = nums.size(), k = n - 1; 
        while(k > 0 && nums[k - 1] >= nums[k]) --k;    // 找到第一对递增序列
        if(k <= 0) reverse(nums.begin(), nums.end());  // 如果整个序列都是递降, 重新排列成最小的排列
        else
        {
            int t = k; 
            while(t + 1 < n && nums[t + 1] > nums[k - 1])  ++t;   // 找到中大于ak-1的最小元素
            swap(nums[t], nums[k - 1]);                           // 交换位置
            reverse(nums.begin() + k, nums.end());                // 逆序处理
        }
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值