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

来源:互联网 发布:mac安装pip ipython 编辑:程序博客网 时间:2024/06/06 05:42

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;    }};
阅读全文
0 0