1.先看一个简单的例子
题干如下:
给定一个数组arr, 和一个数num, 请把小于等于num的数放在数组的左边, 大于num的数放在数组的右边。要求:要求额外空间复杂度O(1), 时间复杂度O(N)。
解法一:
public static int[] changeArrByNum(int[] arr, int num) {
int left = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] <= num) {
//发现小于等于num的数:和left边界交换
int temp = arr[left];
arr[left] = arr[i];
arr[i] = temp;
left++;//left往左的都是<=num的
}
}
return arr;
}
解法二:原理是双指针实现,类似快排划分
public static int[] changeArrByNum2(int[] arr, int num) {
int L = 0;
int R = arr.length - 1;
while (L < R) {
while (arr[R] > num && L < R) {//右边找一个数,<=num
R--;
}
while (arr[L] <= num && L < R) {//左边找一个数,>num
L++;
}
int temp = arr[L];
arr[L] = arr[R];
arr[R] = temp;
}
return arr;
}
2.荷兰国旗问题
荷兰国旗问题:定一个数组arr, 和一个数num, 请把小于等于num的数放在数组的左边,等于num的放中间,大于num的数放在数组的右边。要求:额外空间复杂度O(1),时间复杂度O(n)。
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static int[] partition(int[] arr, int left, int right, int num) {
int L = left - 1;
int R = right + 1;
int cur = left;
while (cur < R) {
if (arr[cur] < num) {
swap(arr, cur++, ++L);//①
} else if (arr[cur] > num) {
swap(arr, cur, --R);//②
} else {
cur++;//③等于num。直接比较下一个数
}
}
return arr;
}
注意:
- 在上述的维护过程中:数组arr[]下标L以及L以左的数据被维护成小于num,下标R以及R以左的数据被维护成大于num,中间的数据是等于num的。
- ①处
swap(arr, cur++, ++L);
,为什么cur++:因为左边已经已经遍历过了,对arr[cur]来说,要么是自己和自己交换,要么是等于num的数和自己交换(主要是因为左边已经遍历过了,情况是已知的)。 - ②处
swap(arr, cur, --R);
,为什么cur没有自加:因为右边交换过来的数字arr[r-1]没有遍历过,不知道>、<还是=,需要进一步判断。