面试常考算法题(二)--荷兰国旗问题
荷兰国旗问题是面试中常考的一个题目,涉及到的思想并不是很复杂.
荷兰国旗问题
题目
给定一个数组 arr,和一个数 num,请把小于 num 的数放在数组的左边,等于 num 的数放在数组的中间,大于 num 的数放在数组的右边。
要求额外空间复杂度 O(1),时间复杂度 O(N)
问题解析
最差解法
这个问题如果不考虑时间复杂度和空间复杂度的话,最简单的解法自然是使用辅助数组,对当前数组数据进行分类以后复制回原数组,代码如下:
public static int[] terriblePartition(int[] arr, int num) {
if(arr == null || arr.length < 2){
return arr;
}
int[] temp = new int[arr.length];
int m = -1;
int n = arr.length ;
//设置两个指针,分别指向temp数组的最前面和最后面,将小于num的数据放在前部分,大于num的数据放在后部分
for (int i = 0; i < arr.length; i++) {
if (arr[i] < num) {
temp[++m] = arr[i];
} else if (arr[i] > num){
temp[--n] = arr[i];
}
}
//等于num的数组数据放在中间
for (int i = m + 1; i < n; i++) {
temp[i] = num;
}
//复制temp数组数据到arr数组
copyArr(arr, temp);
//返回等于num数据的位置
return new int[]{m + 1, n - 1};
}
private static void copyArr(int[] arr, int[] temp) {
for (int i = 0; i < temp.length; i++) {
arr[i] = temp[i];
}
}
这个答案除非题目不要求并且时间不足够思考更优秀的算法,不然不建议使用此方法,这种解法一般仅用作 比较器
.
优秀一点的解法
那么如何在上述方法上进行优化呢,其实本质所使用的思想和上述代码思想一致.知道读者未必对上面的代码感兴趣,这里我来通过一个实际数组[4, 5, 2, 8, 1, 9, 6, 11]进行图解一下:
代码
public static int[] partition(int[] arr, int num) {
if(arr == null || arr.length < 2){
return arr;
}
int less = -1;
int more = arr.length;
int i = 0;
while (i < more) {
if (arr[i] < num) {
swap(arr, ++less, i++);
} else if (arr[i] > num) {
swap(arr, --more, i);
}else {
i++;
}
}
//返回等于num数据的位置
return new int[] { less + 1, more - 1 };
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
后记
这篇文章到这里可以说已经结束了,这个问题中的思想在 快速排序
中可以得到应用,可以用荷兰国旗问题来改进快速排序
使其时间复杂度变为 O(N*logN),额外空间复杂度 O(logN),这个问题将在下一篇文章快速排序中讲.必须要注意的一点是,荷兰国旗问题的解法其实是 不稳定的
,会 改变原数组本可以不改变顺序的数据的相对顺序
.比如[0 9 7 4 4 ]数组最后得到的结果是[0 4 4 7 9 ],数组中的两个 4 实际上交换了位置.
百度百科给出稳定算法的定义如下:
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且 r[i]在 r[j]之前,而在排序后的序列中,r[i]仍在 r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于