荷兰国旗问题是一个经典的排序问题,其目标是将一个包含三种颜色的元素(通常用0、1、2表示)的数组,就地排序成0、1、2三个区域,类似荷兰国旗的红白蓝三色排列。 算法的关键在于只遍历数组一次,就完成排序。 常用的算法是三路快速排序的一种变体,也有人称之为“三指针法”。
问题描述:
给定一个包含0、1、2这三种数字的数组,将其就地排序,使得所有0都在数组的左边,所有1在中间,所有2在数组的右边。
算法详解(三指针法):
该算法使用三个指针:
low
: 指向0区域的末尾(所有小于当前指针的元素都为0)。mid
: 指向当前正在处理的元素。high
: 指向2区域的开头(所有大于当前指针的元素都为2)。
算法流程如下:
-
初始化:
low
指向数组的第一个元素,mid
指向数组的第一个元素,high
指向数组的最后一个元素。 -
循环遍历:
mid
指针从左往右遍历数组。 -
三种情况: 根据
arr[mid]
的值进行判断:arr[mid] == 0
: 交换arr[low]
和arr[mid]
,然后low
和mid
都向右移动一位。arr[mid] == 1
:mid
向右移动一位。arr[mid] == 2
: 交换arr[mid]
和arr[high]
,然后high
向左移动一位。 注意: 此时mid
不移动,因为交换后arr[mid]
的值可能不是2,需要重新判断。
-
循环终止条件: 当
mid
超过high
时,循环结束。
C语言代码实现:
#include <stdio.h>
void dutch_flag_sort(int arr[], int n) {
int low = 0, mid = 0, high = n - 1;
while (mid <= high) {
switch (arr[mid]) {
case 0:
swap(&arr[low], &arr[mid]);
low++;
mid++;
break;
case 1:
mid++;
break;
case 2:
swap(&arr[mid], &arr[high]);
high--;
break;
}
}
}
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int arr[] = {1, 2, 0, 1, 2, 0, 1, 0, 2};
int n = sizeof(arr) / sizeof(arr[0]);
printf("Unsorted array: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
dutch_flag_sort(arr, n);
printf("Sorted array: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
算法复杂度:
- 时间复杂度: O(n),因为每个元素最多被访问一次。
- 空间复杂度: O(1),因为算法是就地排序,不需要额外的空间。
总结:
荷兰国旗问题是一个高效的就地排序算法的经典例子,它展示了如何通过巧妙地使用指针来实现线性时间复杂度的排序。 理解其核心思想和代码实现对于学习算法和数据结构非常有益。 记住关键在于三指针的协调移动和对 arr[mid]
值的三种情况的处理。