「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」。
题目:给定一个包含三种颜色的数组,三种颜色分别用0、1、2表示,要求对这个数组原地排序,使得颜色相同的元素相邻,并且按照颜色顺序012排序。不能使用库已有的sort函数。
解题思路
本题是一个经典的荷兰国旗问题,实际上荷兰国旗就是快速排序的一趟实现,本题可以直接使用一趟的快速排序解决,此时选择的基准值需为1。当然使用任意一种排序方法都可以解决本题,所不同的只是时间复杂度和空间复杂度的不同。另一种思路是在原数组设置大于区域的边界和小于区域的边界,此时数组遍历完了数组排序也完成,边界设置如下:
初始时数组的索引左侧为左边界,数组的索引右边为右边界,当遍历数组元素发现数组元素小于所选的基准值时,数组左边元素和当前元素互换,原索引自增。如果遍历的元素大于所选的基准值,则数组右边元素和当前元素互换,原索引不变,继续比较该索引元素,直至最后原数组即为排序完成的数组,代码如下:
public void sortColors(int[] nums) {
int L=-1;
int R=nums.length;
int cur=0;
while(cur<R){
if(nums[cur]>1){
swap(nums, --R, cur);
}else if(nums[cur]<1){
swap(nums, ++L, cur++);
}else {
cur++;
}
}
}
public void swap(int[] nums, int a, int b){
int temp = nums[a];
nums[a] = nums[b];
nums[b] = temp;
}
上述代码时间复杂度为,空间复杂度为。
快速排序
通过本题可以复习一下快速排序的内容,快速排序每次选定一个基准值,使得数组左边的内容小于此基准值,数组右边的内容大于此基准值,之后递归对左右两边的数组内容继续排序,直到每个数组的元素为1个为止,快速排序的代码如下:
public static void quicksort(int[] arr, int L, int R){
int i = L;
int j = R;
//支点
int pivot = arr[(L + R) / 2];
//左右两端进行扫描,只要两端还没有交替,就一直扫描
while (i <= j) {
//寻找直到比支点大的数
while (pivot > arr[i])
i++;
//寻找直到比支点小的数
while (pivot < arr[j])
j--;
//此时已经分别找到了比支点小的数(右边)、比支点大的数(左边),它们进行交换
if (i <= j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}
//上面一个while保证了第一趟排序支点的左边比支点小,支点的右边比支点大了。
//“左边”再做排序,直到左边剩下一个数(递归出口)
if (L < j)
quicksort(arr, L, j);
//“右边”再做排序,直到右边剩下一个数(递归出口)
if (i < R)
quicksort(arr, i, R);
}
上述代码参考来源:快速排序就这么简单 - 知乎 (zhihu.com)