LeetCode刷题日志(排列与双指针):八

LeetCode-31 下一个排列

题目地址

31. 下一个排列 - 力扣(LeetCode)

题目解析

  • 这个题目相当抽象,以下用通俗语言解释一遍:

    • 对于一个序列[1,2,3],可能存在多个排列:

      • [1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]

    • 这些排列的每一个元素可以连起来看成一个整数

      • [1,2,3]可以看成123

      • [1,3,2]可以看成132

    • 以[1,2,3]为例将其所有的排列转化为整数,并进行升序排序如下:

      • [123,132, 213, 231, 312, 321]

    • 那么[1,3,2]是[1,2,3]的下一个排列,因为它是所有大于[1,2,3]转化的整数的排列转化的整数中最小的

算法解析(参考官方的题解)

  • 则现在的目的是将原序列的整数调大,大家可以参考显微镜的微准焦螺旋:当需要稍微调整显微镜的焦距时,我们会使用微准焦螺旋来适当调整,因为微准焦螺旋旋转一周只会让粗准焦螺旋旋转一个很小的角度,同理低位的数要连续增加到进位才能让高位增加一点(也可以参考钟表的秒针与分针与时针的关系)。

    • 因此为了保证较小的调整,越低位的调整对整体大小的影响越微弱,我们从后往前调试序列,使它大于原序列,且最接近原序列

  • 从右向左寻找一个升序序列,这个升序列是该区间中所有排列中最大的,因此不管对这个序列怎么调整都无法找到比它大的排列。按照第一条的介绍,我们只需要微调这个升序列,就能达到微调整个序列的目的。既然该升序列无法靠这个区间中的数调大,那么就将升序列前的一个数引入进来。

    • 由于从后往前的升序列无法调大,那么将其前面的一个数引进来,构成新序列,此时只能微调这个最高位让整体增大幅度较小

    • 微调不能动这个序列前的数,否则按照第一条的示例,会影响整体的幅度,因此只能在后面的升序列中寻找稍大于序列最高位的数,以达到调整的目的。

      • 找到以后交换最高位的数和(从后向前的)升序列对应数的位置,此时升序列的属性不发生改变,它还是从右往左的升序列(它的逆序是最小的排列

    • 我们调整了高位,为了让这个区间的数和原来对应区间的数相差最小(变化幅度最小),最高位是不能动的,此时把后面的升序列逆序就变成了区间最小的排列,这样整体的变化幅度最小。

      • 如果不好理解的话可以参考数字的进位,由199到200时,最高位要加一,但是后面的位要变得最小即0.

  • 如果升序列前面没有数了,则表示这个序列已经无法调大,按照题目的意思,要把最小的排列返回出去,因此只需要逆序即可。

代码实现

  • 实现分三步

    • 从后向前寻找升序列

    • 引入升序列前得一个数,在升序列中寻找比它稍大的数交换

      • 如果升序列前没有数,这一步不用进行

    • 将升序列逆序

 
class Solution {
 public:
     void iswap(int& a,int& b){
         int c = a;
         a = b;
         b = c;
     }
     void nextPermutation(vector<int>& nums) {
         // 从后往前寻找升序列
         int i;
         int j;
         for(i = nums.size()-1;i>0;--i){
             if(nums[i-1]<nums[i]){
                 // 从后向前的升序列结束,在升序列中寻找可以交换的值
                 for(j = nums.size()-1;j>=i;--j){
                     if(nums[i-1] < nums[j]){
                         iswap(nums[i-1],nums[j]);
                         break;
                     }
                 }
                 break;      
             }
         }
         // 逆序从右向左的升序列[i,nums.size()-1]
         j = nums.size()-1;
         while(i < j){
             iswap(nums[i],nums[j]);
             ++i;
             --j;
         }
     }
 };

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学艺不精的Антон

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值