实际上就是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--;
}
}
}