荷兰国旗问题:Dutch National Flag Problem
a、给定一个序列arr,和一个数字num,把 > num的放在数组右边,把 <= num的放在数组左边
b、给定一个序列arr,和一个数字num,把>num的放在数组右边,把 < num的放在数组左边,等于的放在数组中间
a,b题目的要求:额外空间复杂度O(1),时间复杂度O(N)
思路:
题目a思路:对应leftRightPart()方法
当arr[k] <= num:假定有一个<=的区域(用left = -1代表 <= num的初始区域),k指向当前的值(k从0开始),如果arr[k] <= num,交换当前指向的值与 <= 区域下一位的值,left ++(让<=这个区域往数组右边推移),让k ++,交换的目的是为了让小的值纳入到<=的区域内,这个区域内的经过上述操作后就都是 <= num的值。
当arr[k] > num:这时候直接k ++,直到k走到数组越界为止,整个过程即可完成,k越过的都是比num大的值,k 和 left + 1位置(<=区域下一位)交换的值都是将小的值纳入 <= 区域的过程。
题目b思路:对应leftMidRightPart()方法
当arr[k] < num:假定有两个区域(用left = -1代表小于num的初始区域,用right = arr.length代表大于num的初始区域),k指向当前的值(k从0开始),如果arr[k] < num,arr[k] 和 arr[left+1]的值做交换,然后left ++,k ++
当arr[k] > num:如果arr[k] > num,交换arr[k] 和 arr[arr.length-1]的值,然后right --,right的这个处理是为了将刚才调整到的位置上的值纳入到 > num 的区域内 。 这里arr[arr.length-1] 是 > num 区域的前一位的值,那么交换完,后面的值换到了k的位置,这时候k位置的值不确定它与num的关系,所以让循环的索引 i -= 1,这一步的目的是将已经执行过的一次循环往回调了一下,让换过来的在k位置上的值重新进入判断逻辑。
当arr[k] = num:直接跳过,然后k ++
当k == right时,return,处理完成。
小结:其实b思路就好像在一个桌子上放着一串带有数组的棋子,给定一个规则:确定一个数字,大于这个数字的棋子我拿,小于这个数字的棋子你拿,和这个数字相等的谁也别动。你和我分别代表< num的区域和 > num的区域,这两个区域外侧的都是待处理的数字,待处理区域初始为整个数组。a思路就是b思路的简化.....
package com.kali.quick;
import java.util.Arrays;
/**
* 荷兰国旗问题:Dutch National Flag Problem
* 1.给定一个序列arr,和一个数字num,把 > num的放在数组右边
* 把 <= num的放在数组左边
* 2.给定一个序列arr,和一个数字num,把>num的放在数组右边
* 把 < num的放在数组左边,等于的放在数组中间
* 1,2的要求:额外空间复杂度O(1),时间复杂度O(N)
*/
public class NetherlandsFlagIssue {
public static void main(String[] args) {
int[] arr = {1,3,4,2,7,5,11,2,6,9,8,15,7,7,9,7,7,6,4};
/*int[] process = process(arr, 3);
System.out.println(Arrays.toString(process));*/
/*int[] ints = leftRightPart(arr, 4);
System.out.println(Arrays.toString(ints));*/
int[] tar = leftMidRightPart(arr, 7);
System.out.println(Arrays.toString(tar));
}
//<= num在左边,> num在右边
public static int[] leftRightPart(int[] arr,int num){
int left = -1;
int k = 0;
for (int value : arr) {
if (value <= num) {
swap(arr, k, left + 1);
left++;
}
k++;
}
return arr;
}
//真正意义上的荷兰旗问题 < num在左边,> num在右边 = num在中间
public static int[] leftMidRightPart(int[] arr,int num){
int left = -1;
int right = arr.length;
int k = 0;
for (int i = 0; i < arr.length; i++) {
if(k == right){
break;
}
if (arr[i] < num) {
swap(arr, k, left+1);
left++;
k ++;
}else if(arr[i] > num){
swap(arr,k,right-1);
i -= 1;
right --;
}else{
k ++;
}
}
return arr;
}
public static void swap(int[] arr,int m,int n){
int temp = arr[m];
arr[m] = arr[n];
arr[n] = temp;
}
//按自己想法写的,只实现了<= num在左边,> num在右边,可能不满足复杂度要求
public static int[] process(int[] arr,int wall){
int[] des = new int[arr.length];
int L = 0;
for (int item : arr) {
if (item <= wall) {
des[L] = item;
L++;
}
}
for (int value : arr) {
if (value > wall) {
des[L++] = value;
}
}
return des;
}
}