荷兰国旗问题
问题描述:
给定一个整数数组,给定一个整数值num,这个值在原数组中一定存在,要求把数组中小于num的元素放到数组的左边,大于num的元素放到数组的右边,等于num的元素放到数组的中间,最终返回一个整数数组,其中只有两个值,分别是等于num的数组部分的左右两个下标值。
例如,给定数组:[8, 1, 4, 5, 3, 9, 5, 10, 5],给定一个num值5,那么经过处理原数组可能得一种情况是:[1 4 3 5 5 5 10 9 8 ],需要注意的是,小于5的部分不需要有序,大于5的部分也不需要有序,返回等于5部分的左右两个下标,即[3, 5]
思路:
对数组进行分区,主要分三种情况。
首先明确的是,最终数组从左到右会被划分为:小于num的区间,等于num的区间,大于num的区间
初始时小于num的区间为数组以左,即(-∞,-1),大于num区间为(a.length,+∞)。
1. 当前遍历元素小于num时, 将小于num区间的下一个位置的元素和当前遍历元素交换,然后小于num区间向右扩大一个位置,并且遍历指针也右移一位。
2.当前遍历元素大于num时, 将大于num区间的前一个位置的元素和当前遍历元素交换,然后大于num区间向左扩大一个位置,遍历指针不动!!!因为之前交换来当前遍历位置的元素未被遍历过,并不知道该元素是大于还是小于还是等于num,需要对该元素继续判断,因此遍历指针不动,进入下一次循环,对该元素再进行判断。
3.当前遍历元素等于num时,遍历指针直接向后移动一位。
代码:
public class NetherlandFlag {
public static int[] partition(int[] a, int left, int right, int num) {
/*
* less为小于num区域的右边界,即最终结果left到less为小于num的元素区间
* 初始值设为left-1,遍历中右移
*/
int less = left - 1;
/*
*more为大于num区域的右边界,即最终结果more到right为大于num的元素区间
*初始值设为right+1,遍历中左移
*/
int more = right + 1;
int cur = left;
while(cur < more) {
/*
* 当cur指向的元素小于num时
* 将less所指位置的下一个位置(++less)的元素和cur指向元素交换
* 然后cur指针前进
*/
if(a[cur] < num) {
/*
* less++;
* swap(a, less, cur);
* cur++;
*/
swap(a, ++less, cur++);
}
/*
* 当cur指向的元素大于num时
* 将more所指位置的前一个位置(--more)的元素和cur指向元素交换
* 然后cur指针不动!!!!
* 此时换过来的元素有可能小于num或等于num或大于num 需要继续进入下次循环
*/
//注意!!!这里要写else if,才是三种情况挑一种执行
else if(a[cur] > num) {
swap(a, --more, cur);
}
//当cur指向元素等于num时,cur指针直接向后移动
else {
cur++;
}
}
//结果返回一个包含两个元素的数组,表示等于num的元素区间为[less+1, more-1]
return new int[] {less + 1, more - 1};
}
private static void swap(int[]