leetcode 31. Next Permutation (下一个排列,模拟,二分查找)

题目链接

31. Next Permutation

题意

给定一段排列,输出其升序相邻的下一段排列。比如[1,3,2]的下一段排列为[2,1,3]。
注意排列呈环形,即[3,2,1]的下一段排列为[1,2,3]

思路

这个题蛮巧妙的,关键在于发现规律。假如给定的排列为[4,6,7,5],那么其下一段排列应该为[4,7,5,6]
我们可以看到除了首位的4保持不动外,后三位均发生了改变。我们可以把[4,6,7,5]看成是[4,6,5,7] -> [4,7,5,6]
这里的6称为旋转点,我们先把旋转点右边的升序([6,7,5] -> [6,5,7]),然后从升序的子数组里找到到第一个大于旋转点的值,然后交换两者的值

为什么要这么处理呢?我的一个理解是要保证相邻排列是升序的,不如先把旋转点右边的值先排序,至少保证了局部有序,
然后从局部有序的右边数组里面采用二分查找,找到第一个大于旋转点的值,交换即可满足题意。

那怎么定义旋转点呢?
可以从最右边开始往左边找,第一个小于右边相邻的数即为旋转点,如[4,6,7,5]从右边往左边找,第一个小于右边相邻的数为6(6<7, 7>5)

class Solution {
public:
    //这个题,怎么说呢
    //在于观察吧
    //2 4 7 6-> 2 4 6 7->2 6 4 7
    //模拟
    void nextPermutation(vector<int>& nums) {
        int n=nums.size();
        if(n<=1) return ;
        
        int j=n-2;
        
        for(;j>=0;j--){//找到旋转点
           
            if(nums[j]<nums[j+1]) break;
        }
        
        //把旋转点右边的数组升序
        reverse(nums.begin()+j+1,nums.end());
        
        if(j==-1) return ;
        
        //找到和旋转点可以替换的点
        auto it=upper_bound(nums.begin()+j+1,nums.end(),nums[j]);
        
        swap(*it,nums[j]);
        
    }
};
posted @ 2020-10-29 16:37  xzhws  阅读(54)  评论(0编辑  收藏  举报