常考的技巧类算法题(一):下一个排列

技巧类算法题是我随口提的一个概念,意思就是这道题有自己独特的思考思路,仅仅知道它所涉及的最基础的知识点(如宽泛的双指针,动规或深度优先搜索),然后自行推理解题套路十分困难。因此在此做一个整理,面试前过一遍

题目链接:下一个排列

题目描述

整数数组的一个排列就是将其所有成员以序列或线性顺序排列。下一个排列是指字典序更大的所有排列中,最小的那一个。如果这个排列已经是这几个元素所能组成的最大的字典序时,返回字典序最小的排列。

例如,123的下一个排列是132,321的下一个排列是123,1511的下一个排列是5111

传递的参数是vector的引用,因此只能在原数组上修改,空间复杂度为O(1)

问题分析

你要找下一个排列,就需要让一个大一点的数和一个小一点的数交换位置。但是你又希望字典序增大幅度尽可能地小,所以如果将vector看作一个数,你应该优先动数位较低的数,即应该从vector后面往前遍历。

那什么时候停止遍历开始进行下一步操作呢?当然是目前所访问的数,大于数组中索引比他小一位的数。这一段略抽象,举个例:

[1,2,3,6,5,4,1]

在这个数组中,如果你在654这三个数中随便交换两个,他们都会变大,因为在这个范围内,nums[i]>nums[i+1],因此要找到nums[i]<nums[i+1]的地方,才能开始交换树的位置。

易知,当访问到3和6时,开始下一步。下一步,我们要决定谁和3进行交换,如果是3和6互换,那么nums[2]的位置会急剧变大,显然不是字典序只相差一位。可如果在3后面的数中找出最小的那一个,那会把最末尾的1和3互换,字典序反而变小了。显然,我们要找到一个刚好比3大的数。

因此,从数组末尾遍历,找到第一遇到的比3大的数就行。因为3后面的数是降序,所以遇到第一个比3大的数之后,加下来遍历的数只会更大,因此可以直接break跳出循环。

在结束循环后,还有一个小细节,3后面的那一块排列,有可能变小吗?当然,把1移到6的位置就能让排列变小。既然只交换3和4后还能变小,就说明单纯地交换之后不是我们所要的结果,为了让后面的区间的排列尽可能小,可以通过sort函数使其变为升序排列。

最后,如果找不到任何一组相邻的数是升序该怎么办呢?很简单,反转数组就行。

代码

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int n=nums.size()-1;
        //寻找第一次出现逆序的相邻两数
        //从数组尾部找起,相当于从数字较低的位找起
        for(int i=n;i>=1;i--){
            //满足if,说明找到了第一次出现升序的相邻数对
            if(nums[i]>nums[i-1]){
                //再次从末尾找起,寻找刚好比i-1要大的数
                for(int j=n;j>=i;j--){
                    //找到了可以直接break,此时i-j之间的数一定比nums[j]大
                    if(nums[j]>nums[i-1]){
                        int temp=nums[j];
                        nums[j]=nums[i-1];
                        nums[i-1]=temp;
                        break;
                    }
                }
                //最后,将i-1后面的部分进行排序,使他们的字典序尽可能小
                sort(nums.begin()+i,nums.end());
                return;
            }
        }
        //因为满足for里面的if后,必定会执行return
        //所以函数但凡执行到这里,就说明nums完全单调递减,字典序达到了最大
        //此时将数组反即可
        //不要用排序,毕竟排序要多次比较,玩意sort底层算法用了快排,时间复杂度直接O(nlogn)
        //而反转函数完全不比较大小,O(n)时间复杂度搞定
        reverse(nums.begin(),nums.end());
        return;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值