下一个排列

实际上就是c++中函数next_permutation的实现(包含重复元素)

c++源码实现

网址:https://en.cppreference.com/w/cpp/algorithm/next_permutation

template<class BidirIt>
bool next_permutation(BidirIt first, BidirIt last)
{
    if (first == last) return false;
    BidirIt i = last;
    if (first == --i) return false;
 
    while (true) {
        BidirIt i1, i2;
 
        i1 = i;
        if (*--i < *i1) {
            i2 = last;
            while (!(*i < *--i2))
                ;
            std::iter_swap(i, i2);
            std::reverse(i1, last);
            return true;
        }
        if (i == first) {
            std::reverse(first, last);
            return false;
        }
    }
}


思路

两次从后扫描,第一次找较小数,第二次找较大数

如何将一个数交换某两位位置让它变大,简单思考就是将左边的小数和右边的大数相交换就可以了。

例如:12345将3和5互相交换可以得到12543,如何让12543比12543小但比12345大呢,可以将12543从5往后面的数进行升序即12534

所以现在得到一个有用信息,交换后要将左边数位置之后的数进行升序。

如何确定应该交换哪两个数可以得到较小的值?

首先12345交换的两位越靠右得到的值比较小,例如交换1和5交换和4和5交换差的不是一点半点。

所以两个数的搜索都应该从数组后面向前进行遍历查找,并且较小数越靠右越好。

题解

class Solution {
    public void nextPermutation(int[] nums) {
        int i = nums.length - 2;
        //找到较小数要靠右(从后面扫一遍)
        //找到较大数要尽可能小
        //交换较大数较小数,对较小数位置后面的数进行升序处理保证最小
        while(i >= 0 && nums[i] >= nums[i+1]){
            i--;
        }
        //[i+1,n]为降序
        if(i >= 0){
        int j = nums.length - 1;
        while(j >= 0 && nums[i] >= nums[j]){
            j--;
        }
        swap(i,j,nums);
        }

        reverse(i+1,nums.length-1,nums);


        
    }
    public void swap(int i,int j,int[] nums){
        int tmp  = nums[i];
        nums[i] = nums[j] ;
        nums[j] = tmp;
    }
    public void reverse(int start,int end,int[] nums){
        if(start >= end) return ;
        while(start < end){
            swap(start,end,nums);
            start++;
            end--;
        }
    }
}


posted @ 2020-11-10 15:40  浅滩浅  阅读(168)  评论(0编辑  收藏  举报