每天一道LeetCode-----重新实现next_permutation

Next Permutation

原题链接Next Permutation
这里写图片描述
重新实现next_permulation函数

template <class BidirectionalIterator>
bool next_permutation (BidirectionalIterator first,
                         BidirectionalIterator last);

next_permulation,接受两个迭代器,表示区间[first, last],将这个区间排列成下一个较大的序列,如果当前区间已经是最大的序列(降序),则返回false。
next_permulation直接在原区间上更改,比如

#include <iostream>

#include <algorithm>
#include <vector>
#include <iterator>

int main()
{
    using namespace std;
    vector<int> nums{1, 2, 3, 4};
    do
    {
        copy(nums.begin(), nums.end(), ostream_iterator<int>(cout, " "));
        cout << endl;
    }while(next_permutation(nums.begin(), nums.end()));

    return 0;
}

输出结果

1 2 3 4 
1 2 4 3 
1 3 2 4 
1 3 4 2 
1 4 2 3 
1 4 3 2 
2 1 3 4 
 ...
4 2 3 1 
4 3 1 2 
4 3 2 1 

注意next_permutation每次只能找比当前序列大的下一个序列,如果没有就返回false。并不是打印所有的排序。如果nums初始化为{2, 1, 3, 4},那么就只会从{2, 1, 3, 4}这个序列开始找,不会有{1, *, *, *}这样的排列


题目要求就是实现next_permutation函数,返回当前序列的下一个序列(排列),要求比当前序列大,但是是所有可能的结果中最小的,比如说

寻找1 3 2的下一个排序
返回的应该是2 1 3而不是2 3 1,因为2 1 3是所有可能中最小的

这也是next_permutation的规则,每次只找比当前排列大的所有可能的结果中最小的那个排列
既然要变大,又要让变大的幅度尽量小,就需要尽量让前面的数字保持不变,只改变后面几个的顺序。拿数字来说就是尽量只改变个位十位百位再往上的顺序,尽量不改变万位,十万位,百万位那些高位的顺序,这样才能让变大的幅度小一些。所以很显然要从后往前遍历。

思路如下,假设nums大小为n

  1. 从后向前遍历,i记录当前遍历到的位置
  2. 因为只有当递减时才没有更大的排序,所以[i + 1 : n)一定是递减的
  3. 如果[i + 1 : n)中有比nums[i]大的元素,找到最后一个比nums]i]大的元素(因为递减),与nums[i]互换位置
  4. 此时nums[i]已经比原先的nums[i]大,高位变大,低位应该尽量变得最小
  5. 将[i + 1 : n)逆序,因为以前递减,互换后仍然递减,为了变小,应该让后面序列递增

代码如下

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int n = nums.size();
        /* 从倒数第二个开始,判断后面有没有比nums[i]大的数 */
        for(int i = n - 2; i >= 0; --i)
        {
            /* 因为[i+1: n)是递减的,如果大于最大的那个,就说明不存在比nums[i]大的 */
            if(nums[i] >= nums[i + 1])
                continue;
            /* 二分查找找到最后一个比nums[i]大的位置 */
            int j = binary_find(i + 1, n - 1, nums, nums[i]);
            /* 交换位置,让高位变大 */
            swap(nums[i], nums[j]);
            /* 逆序,高位变大后,让低位变得最小 */
            reverse(nums.begin() + i + 1, nums.end());
            return;
        }
        /* 如果整个nums最开始就是递减的,那么没有更大的排序,变成最小的 */
        reverse(nums.begin(), nums.end());
    }
private:
    /* 二分查找,找到最后一个大于n的位置 */
    int binary_find(int left, int right, vector<int>& nums, int n)
    {
        /* 
         * left左边一定都大于n,right右边一定都小于等于n。返回后left > right
         * 所以nums[right] > n, nums[left] <= n
         * 所以nums[right]就是最后一个大于n的数
         */
        while(left <= right)
        {
            int middle = (left + right) / 2;
            /* 防止无限循环,所有不管大于/小于都改变其中一个大小 */
            /* 如果中间位置大于n,说明在右边,但是middle有可能也是最后的结果 */
            if(nums[middle] > n)
                left = middle + 1;
            /* 如果小于等于n,说明在左边 */
            else
                right = middle - 1;
        }
        return right;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值