三指针排序是一种特殊的分区排序算法,通过使用三个指针同时操作数组,将元素按照特定规则进行分类和排序。这种算法在处理包含有限种类值的数组时表现出色,最经典的应用是荷兰国旗问题(Dutch National Flag Problem)。
三指针排序的核心思想是:
这是最常见的三指针排序应用,将数组中的元素按照特定值分为三类:
处理有三种不同元素的数组,如荷兰国旗问题中的红、白、蓝三色:
三指针排序主要操作简单数组,不需要特殊的数据结构设计,只需要三个指针变量:
int low = 0; // 指向第一类元素(0)的右边界
int mid = 0; // 遍历指针,指向当前处理的元素
int high = n - 1; // 指向第三类元素(2)的左边界
public class ThreePointerSort {
/**
* 三指针排序算法(荷兰国旗问题)
* 将数组中的0、1、2三种元素排序
*/
public void sortColors(int[] nums) {
int low = 0; // 0的右边界
int mid = 0; // 当前处理元素
int high = nums.length - 1; // 2的左边界
while (mid <= high) {
if (nums[mid] == 0) {
// 当前元素为0,将其交换到左侧区域
swap(nums, low, mid);
low++;
mid++;
} else if (nums[mid] == 1) {
// 当前元素为1,保持位置不变
mid++;
} else { // nums[mid] == 2
// 当前元素为2,将其交换到右侧区域
swap(nums, mid, high);
high--;
// 注意:这里mid不递增,因为交换后的元素需要重新检查
}
}
}
/**
* 交换数组中两个元素的位置
*/
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
三个区域划分:
[0, low-1]
:所有等于0的元素[low, mid-1]
:所有等于1的元素[mid, high]
:待处理的元素[high+1, n-1]
:所有等于2的元素处理逻辑:
low
和mid
都向右移动mid
high
向左移动,mid
不动(需要重新检查交换后的元素)终止条件:
mid > high
时,所有元素都已处理完毕以数组 [2, 0, 1, 2, 1, 0]
为例:
low=0, mid=0, high=5
[2, 0, 1, 2, 1, 0]
nums[mid]=2
nums[mid]
和 nums[high]
:[0, 0, 1, 2, 1, 2]
high=4
, mid=0
(不变)nums[mid]=0
nums[low]
和 nums[mid]
:[0, 0, 1, 2, 1, 2]
(实际未变)low=1
, mid=1
nums[mid]=0
nums[low]
和 nums[mid]
:[0, 0, 1, 2, 1, 2]
(实际未变)low=2
, mid=2
nums[mid]=1
mid=3
nums[mid]=2
nums[mid]
和 nums[high]
:[0, 0, 1, 1, 2, 2]
high=3
, mid=3
nums[mid]=1
mid=4
mid=4 > high=3
,算法终止
[0, 0, 1, 1, 2, 2]
三路快排是三指针思想的另一个重要应用:
public void quickSort3Way(int[] arr, int low, int high) {
if (low >= high) return;
// 选择基准值
int pivot = arr[low];
int lt = low; // 小于区域右边界
int gt = high; // 大于区域左边界
int i = low + 1; // 当前处理元素
while (i <= gt) {
if (arr[i] < pivot) {
swap(arr, lt++, i++);
} else if (arr[i] > pivot) {
swap(arr, i, gt--);
} else {
i++;
}
}
// 递归排序小于和大于部分
quickSort3Way(arr, low, lt - 1);
quickSort3Way(arr, gt + 1, high);
}
三指针排序可用于将数组按特定规则分为三组,如:
当需要找出数组中属于特定值范围的所有元素时,可使用三指针方法快速划分:
public int[] findElementsInRange(int[] nums, int low, int high) {
// 使用三指针划分数组
int[] result = new int[nums.length];
int count = partitionByRange(nums, low, high);
// 复制中间区域元素并返回
// ...
}
private int partitionByRange(int[] nums, int lowVal, int highVal) {
int left = 0;
int curr = 0;
int right = nums.length - 1;
while (curr <= right) {
if (nums[curr] < lowVal) {
swap(nums, left++, curr++);
} else if (nums[curr] > highVal) {
swap(nums, curr, right--);
} else {
curr++;
}
}
return right - left + 1; // 返回范围内元素数量
}
public class ThreeColorSort {
public static void main(String[] args) {
int[] colors = {2, 0, 1, 1, 0, 2, 1, 2, 0, 0, 1, 2};
System.out.println("排序前: " + arrayToString(colors));
sortColors(colors);
System.out.println("排序后: " + arrayToString(colors));
}
public static void sortColors(int[] nums) {
int low = 0; // 0的右边界
int mid = 0; // 当前处理元素
int high = nums.length - 1; // 2的左边界
while (mid <= high) {
if (nums[mid] == 0) {
swap(nums, low++, mid++);
} else if (nums[mid] == 1) {
mid++;
} else { // nums[mid] == 2
swap(nums, mid, high--);
}
}
}
private static void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
private static String arrayToString(int[] arr) {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]);
if (i < arr.length - 1) {
sb.append(", ");
}
}
sb.append("]");
return sb.toString();
}
}
public class ThreeWayPartition {
public static void main(String[] args) {
int[] arr = {5, 2, 8, 12, 3, 6, 9, 4, 10, 7};
int lowVal = 4;
int highVal = 8;
System.out.println("原数组: " + arrayToString(arr));
System.out.println("划分范围: [" + lowVal + ", " + highVal + "]");
partitionByRange(arr, lowVal, highVal);
System.out.println("划分后: " + arrayToString(arr));
}
public static void partitionByRange(int[] nums, int lowVal, int highVal) {
int left = 0;
int curr = 0;
int right = nums.length - 1;
while (curr <= right) {
if (nums[curr] < lowVal) {
swap(nums, left++, curr++);
} else if (nums[curr] > highVal) {
swap(nums, curr, right--);
} else {
curr++;
}
}
}
private static void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
private static String arrayToString(int[] arr) {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]);
if (i < arr.length - 1) {
sb.append(", ");
}
}
sb.append("]");
return sb.toString();
}
}
三指针排序算法是一种高效、简洁的算法,特别适合处理有限种类值的排序问题。
核心要点:
优点:
缺点:
应用场景:
三指针排序是分治思想和指针技术的巧妙结合,体现了算法设计中"简单而高效"的理念。掌握这一技术不仅有助于解决特定问题,更能启发我们思考更广泛的算法设计方法。