这个题想了好久没想出来,后来查了一下,原来是STL里的一个函数,直接给前辈跪了。
要想在O(N)的时间内求得,必须找到当前排列与后一个排列之间的关系。举几个例子,13245的下一个排列是13254,15432的下一个排列是21345,有没有什么联系呢?我觉得不说出来的话是很难发现的。后一个排列一定是由前一个排列置换得到的,最简单的情况,12345,下一个排列是12354,置换的一定是原来是顺序的那两个数字,置换之后,他们变成逆序的。顺序对可能有很多,应该置换哪一对儿呢?答案是最相近的那一对,这个相近指的不是他们在位置上的相近,而是数值上的相近,道理很简单,下一个排列在置换之后要增加的最小。那怎样找一对顺序又相近的数字呢?方法是从后往前找,不断比较两个相邻元素是不是顺序的,不是顺序的就继续往前(说明这一小段是从后往前递增的,也就是逆序的),知道找到顺序对或者到达最开始,到达最开始都不满足的话,说明整个序列是逆序的,按照要求,应该把所有元素都逆序一下。如果找到这样一个顺序对,那么应该置换的不是这两个元素,而应该是从尾部找的,第一个比顺序对中的前面的那个元素(较小的那个)大的元素,来跟这个元素做置换。举个例子,15432的第一个顺序对是15,较小的数是1,不应该置换15,因为5可能大太多了,而是从尾部找第一个比1大的,就是2,置换之后,得到的是25431。结束了吗?当然没有,刚刚置换过的两个元素之间的这部分一定是递增的,当一个较大的被换到前面之后,它后面的应该恢复顺序排列才行,所以要把之间的这部分,包括被换到后面的那个数,逆序排列。
我觉得这个还是比较难理解的,从头开始找规律也很困难,只能自己找例子多跑几遍。看了源码的感觉就是”不能赞一词“,严谨精确。
class Solution {
public:
void nextPermutation(vector<int> &num) {
vector<int>::iterator first = num.begin(), last = num.end(), i, ii, j;
if(first == last) return;
i=first;
++i;
if(i == last) return;
i = last;
--i;
while(true){
ii = i;
--i;
if(*i<*ii){
j = last;
while(*(--j)<=*i);
iter_swap(i, j);
reverse(ii, last);
return;
}
if(i == first){
reverse(first, last);
return;
}
}
}
};