LeetCode中等:下一个排列(C#)
题目描述:实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
思路:乍一看题目感觉挺复杂的,直接求下一个排列好像一下子没什么思路。所以从最简单的情况入手——数列只有两个数。此时,若第一个数大于第二个,则属于不存在下一个更大的排列的情况,直接输出最小排列即可;反之,则应当交互这两个数。事实上,对于任意的数列,只要最后两个数是正序排列的,那么交换最后两个数即可。
假设数列Nums={A0,A1,A2……An}。将Numsmin{An-1,An}(也就是最后的两个数)作为最小数列,将Numscurrent={Am,Am+1……An}称为当前数列。则有以下流程:
(流程图画的有点粗糙。。。)
需要注意的是虚线框里的部分,根据前面的条件,此时的Numscurrent中Am+1大于Am且大于Am+1后面的数。这样其实可以分为三种情况继续讨论,这里就不多赘述了。这三种情况对应的做法可以统一成一种做法,也就是虚线框里写的。多提一嘴,这种情况下,Am+1及其后面的数字必然是降序排列的,在编程的时候利用这种特性可以更加高效。
public void NextPermutation(int[] nums)
{
if (nums.Length == 0)
return;
int pos = 0;
if (nums[nums.Length - 2] < nums[nums.Length - 1])
exchange(ref nums, nums.Length - 2, nums.Length - 1);
else
{
for (int i = nums.Length - 3; i >= 0; i--)
{
if (nums[i] >= nums[i + 1])
continue;
else
{
for (int j = i+1; j < nums.Length; j++)
if (nums[j]>nums[i])
pos = j;
exchange(ref nums, pos, i);
quick_sort(ref nums, i + 1, nums.Length - 1);
return;
}
}
Array.Reverse(nums);
}
}
public static void exchange(ref int[] nums,int a,int b)
{
int c = nums[a];
nums[a] = nums[b];
nums[b] = c;
}
public static int[] quick_sort(ref int[] list, int left, int right)
{
if (left < right)
{
int mid = get_partition(ref list, left, right);
quick_sort(ref list, left, mid - 1);
quick_sort(ref list, mid + 1, right);
}
return list;
}
private static int get_partition(ref int[] list, int left, int right)
{
int tmp = list[left];
while (left < right)
{
while (left < right && list[right] >= tmp)
{
right -= 1;
}
list[left] = list[right];
while (left < right && list[left] <= tmp)
{
left += 1;
}
list[right] = list[left];
}
list[right] = tmp;
return right;
}
代码里的quick_sort()函数是快排。实际上从大到小排列可以不用快排而是对目标数列进行反转,不过懒得改了。。