题目描述
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须 原地 修改,只允许使用额外常数空间。
示例1
输入:nums = [1,2,3]
输出:[1,3,2]
示例2
输入:nums = [3,2,1]
输出:[1,2,3]
解决方案:一次扫描+双指针法
叭叭几句
先说一下 至于为什么不用暴力法进行对数组进行逐一循环遍历,把所有可能都获取出来,每个都去拼接一下的原因,时间复杂度太高了,这道题的使用暴力法破解的话,时间复杂度就会出现一个阶乘复杂度,面试的人看了都害怕哈哈哈,而且题目限制了只能额外使用常数空间,所以不能像两数之和一样使用哈希表转存。
难点
难点1:如何理解题目的下一个排列和字典序
字典序就是从小到大排序 如 1 2 3 4等等,下一个排序 指的就是将当前这个数组 进行全部排序组合,找到字典序中的下一个排序组合,说人话就是找出来比传入的值大一点的排序组合,画个图方便理解!
难点2:这个有没有什么技巧去解这道题
肯定是有的 通过观察就可以发现 下一个排序其实就是尽量替换低位 即替换百位数字可以实现 千万不要替换千万即可,下面画图详细解释。
解题思路
我们先规定一个概念 叫做正序子序列,这个正序子序列指的就是再一个数组中,找到正序排序的一段子数组。 如[3,4,5,2],则[4,5]就称之为这个数组的正序子序列。
画一个图详细解释我们这个题目的解答思路
这个图片中 就代表我们传入的数组对应的折线图,既然已经分析出来了,我们这道题,就是尽量替换低位上的一个值,换大一点就可以实现找到下一个排序了
- 以从后往前遍历数组,找到第一个正序子序列的开头,替换这个值即可实现替换最低位,如图所示的位置
既然找到了数组下标为3的值,于是将这个值标记为k,要拿一个别的值去替换k,我们在这里就要考虑拿什么值去代替这个k,既然题目的意思是要找原数组的下一个排列,所以肯定要把大于k 并且再后半段是最小的值找到并且拿去替换 否则替换出来的值 就不是最小的了,不满足题意。
2. 找到k之后要做一个特殊处理的判断,由于k是倒序遍历出来的 最后一次循环时 k=0,并且还会执行一次自减操作 如果k = -1 则代表没有找到相关的正序子序列,即这个数组是倒序排列的,倒叙排列的数组 就是最大的,也就不存在下一个排列了,所以直接调用Arrays.sort排正序即可
3. 找到在k之后那一段的数组中,大于k并且是那一段中的最小的值 这个值记为i,i的作用是和k位置的值进行替换。
4. 将i和k位置的值对调 剩下的后半段由于有序(这里有序的原因是因为最初找k的时候 如果不满足nums[k]>nums[k+1] 那样就不会继续执行了 所以k之后的半段 一定有序) 进行一个双指针的对应位置替换即可实现找到下一个排列。
代码实现:
class Solution {
public void nextPermutation(int[] nums) {
int n = nums.length;
// 1. 从后往前寻找第一次出现正序子序列的位置k k即我们要替换掉的位置
// 1.1 数组的倒数第一个不用检查 所以跳过
int k = nums.length - 2;
// 1.2 遍历数组 找到正确的k的位置
while (k >= 0 && nums[k] >= nums[k + 1]) // O(n)
k--;
// 2. 特殊情况处理
// 2.1 过滤数组不存在更大的排序的情况 上面寻找k的位置 k执行的最后一次是k=0 如果还没找到k会变为-1 所以直接判断k是否=-1即可
if (k == -1) {
Arrays.sort(nums); // O(logN)
return;
}
// 3. 正常情况 找到了K的位置 既然要求下一个更大的排序 所以必须要拿后半段的数组中 大于k并且是最小的值去替代k
// 3.1 寻找i的位置
int i = k + 2;
while (i < n && nums[i] > nums[k]) // O(n)
i++;
// 执行到这里的i 由于目前数组后半段是有序的 nums[i]<k && nums[i-1]>k
// 3.2 数字进行交换
int temp = nums[k];
nums[k] = nums[i - 1];
nums[i - 1] = temp;
// 截止到目前为止 k位置上的数字 已经和i位置上的数字进行了替换
// 3.3 将k之后的序列 都变为升序(目前是降序) 从而实现最小值排序 使用双指针进行替换
int startPoint = k + 1;
int endPoint = n - 1;
while (startPoint < endPoint) { // O(n)
// 逐一替换
int tempNum = nums[startPoint];
nums[startPoint] = nums[endPoint];
nums[endPoint] = tempNum;
// 指针偏移
startPoint++;
endPoint--;
}
}
}
作者:cgadmin544
链接:https://leetcode-cn.com/problems/next-permutation/solution/java-shi-yong-yi-ci-sao-miao-shuang-zhi-nqu2r/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
复杂度分析
时间复杂度
本程序中共有三次循环 皆跟n有关 又因为循环没有嵌套关系 所以复杂度为O(N) 排序使用的是sort 底层是快排 O(LogN) 所以时间复杂度是O(N)
空间复杂度
只有固定个数的变量用作替换 所以空间复杂度是O(1)
本题目感觉分析的不是很完美,如果看代码还是有不懂的话可以留言或者私聊,我会细心解答的呢。
完美解决这个问题 要求常数空间复杂度 并且实现了线性时间复杂度 还是有点小骄傲哈哈哈!
作者的话
希望各位观看我文章的人不要感觉我比较墨迹哈,因为我也是属于基础比较差,所以把所有人都当做和我同样的起点去看待,用尽量详细的方式给大家讲懂,也给自己梳理一遍思路。
开本专栏的目的其实是为了自己的大厂梦吧! 也算为了让大家见证我这个小菜鸡的逐渐成长! 如果本文章可以帮助到您一点点,本人万分荣幸!! 如有任何问题请给我留言,我会逐一回复的哈。
全面发展,一专多能!!!