LeetCode31-下一个排列
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
一、思路
(一)移动数
先来阐述一下字典序的概念:
1、字典序
设P是1~n的一个全排列:
p
=
p
1
p
2
.
.
.
.
.
.
p
j
−
1
p
j
p
j
+
1
.
.
.
.
.
.
p
k
−
1
p
k
p
k
+
1
.
.
.
.
.
.
p
n
p=p_1p_2......p_{j-1}p_jp_{j+1}......p_{k-1}p_kp_{k+1}......p_n
p=p1p2......pj−1pjpj+1......pk−1pkpk+1......pn
(1)从排列的右端开始,找出第一个比右边数字小的数字的序号
j
j
j(
j
j
j从左端开始计算),即
j
=
m
a
x
{
i
∣
p
i
<
p
i
+
1
}
j=max\{i|p_i<p_{i+1}\}
j=max{i∣pi<pi+1}
(2)在
p
j
p_j
pj的右边的数字中,找出所有比
p
j
p_j
pj大的数中最小的数字
p
k
p_k
pk,即
k
=
m
a
x
{
i
∣
p
i
>
p
j
}
k=max\{i|p_i>p_j\}
k=max{i∣pi>pj}(右边的数从右至左是递增的,因此
k
k
k是所有大于
p
j
p_j
pj的数字中序号最大者)
(3)对换
p
j
,
p
k
p_j,p_k
pj,pk
(4)再将
p
j
+
1
.
.
.
.
.
.
p
k
−
1
p
k
p
k
+
1
.
.
.
.
.
.
p
n
p_{j+1}......p_{k-1}p_kp_{k+1}......p_n
pj+1......pk−1pkpk+1......pn倒转得到排列
p
′
=
p
1
p
2
.
.
.
.
.
p
j
−
1
p
j
p
n
.
.
.
.
.
p
k
+
1
p
k
p
k
−
1
.
.
.
.
.
p
j
+
1
p'=p_1p_2.....p_{j-1}p_jp_n.....p_{k+1}p_kp_{k-1}.....p_{j+1}
p′=p1p2.....pj−1pjpn.....pk+1pkpk−1.....pj+1,这就是排列
p
p
p的下一个排列。
2、算法流程
(1)从右边开始往左查找,找到第一个顺序对(即
j
=
m
a
x
{
i
∣
p
i
<
p
i
+
1
}
j=max\{i|p_i<p_{i+1}\}
j=max{i∣pi<pi+1})
如果找不到,则直接将整个排序逆转
(2)从
p
j
+
1
.
.
.
.
.
.
p
k
−
1
p
k
p
k
+
1
.
.
.
.
.
.
p
n
p_{j+1}......p_{k-1}p_kp_{k+1}......p_n
pj+1......pk−1pkpk+1......pn中找出所有比
p
j
p_j
pj大的数中最小的数字
p
k
p_k
pk
(3)对换
p
j
,
p
k
p_j,p_k
pj,pk
(4)再将
p
j
+
1
.
.
.
.
.
.
p
k
−
1
p
k
p
k
+
1
.
.
.
.
.
.
p
n
p_{j+1}......p_{k-1}p_kp_{k+1}......p_n
pj+1......pk−1pkpk+1......pn倒转得到排列
p
′
=
p
1
p
2
.
.
.
.
.
p
j
−
1
p
j
p
n
.
.
.
.
.
p
k
+
1
p
k
p
k
−
1
.
.
.
.
.
p
j
+
1
p'=p_1p_2.....p_{j-1}p_jp_n.....p_{k+1}p_kp_{k-1}.....p_{j+1}
p′=p1p2.....pj−1pjpn.....pk+1pkpk−1.....pj+1,这就是排列
p
p
p的下一个排列。
C++代码:
class Solution {
public:
void nextPermutation(vector<int>& nums) {
for (int i = nums.size() - 1; i > 0; i--) {
// 从右往左,找到第一个小于右边的j
if (nums[i - 1] < nums[i]) {
for (int j = nums.size() - 1; j >= i; j--) {
if (nums[j] > nums[i - 1]) {
int num = nums[j];
nums[j] = nums[i - 1];
nums[i - 1] = num;
break;
}
}
reverse(nums, i, nums.size() - 1);
return;
}
}
reverse(nums, 0, nums.size() - 1);
return;
}
void reverse(vector<int>& nums, int begin, int end) {
// 这是一个闭合区间
int num;
while(begin<end) {
num = nums[begin];
nums[begin] = nums[end];
nums[end] = num;
begin++;
end--;
}
return;
}
};
执行效率: