在Partition 算法详解 + 应用 + 代码实现(一)中,我们解释了Partition的算法思路和基本代码实现,在本章节中,我们讨论Partition的应用(快排,寻找数组中第K个大的数以及经典的红蓝白三球问题)以及代码实现,【话说我至今才知道Partition的中文解释是分区、分治。。。】。
代码应用1:快排
Partition最战功显著的应用就是快排,快排的主要思想就是分治+Partition。大家都知道快排的核心思想是“分而治之”,它将一个大数组的排序问题拆分成几个不同的小的数组排序问题进行求解;但是这里要注意,如何进行数组的划分,这就是Partition的主要功能,它可以通过一次扫描将数组划分为大小两个部分,然后再对两个部分分别进行处理。当检测到每个子数组已经排序完毕【数组头和数组尾重叠】时,排序已经结束。
话不多说,直接上代码:
//快排正向游走
int qs_partition_pos(int arr[], int beginn, int endd, int privot_posisiton){
if (privot_posisiton >= endd && privot_posisiton < beginn){
return 0;
}
int privot = arr[privot_posisiton];
int pos = beginn;
for (int i = beginn + 1; i < endd; i++){
if (arr[i] <= privot){
pos++;
if (pos < i){
swap(arr[i], arr[pos]);
}
}
/*
cout<<"i = "<<i<<" ; pos = "<<pos<<" ; ";
for (int j = beginn; j < endd; j++){
cout<<arr[j]<<" ";
}
cout<<endl;
*/
}
swap(arr[pos], arr[privot_posisiton]);
return pos;
}
void quicksort(int arr[], int beginn, int endd){
if (beginn >= endd - 1){
return;
}
int pos = qs_partition_pos(arr, beginn, endd, beginn);
quicksort(arr, beginn, pos);
quicksort(arr, pos + 1, endd);
}
快排相当于扫描数组一次之后再进行二分处理,所以时间复杂度为O(Nlog(N))。
Partition应用2:找到无序数组中最大的第K个数
这个请参考博客剑指offer——最小的K个数字,其实就是扫描一次后看排序后左边的数组长度是否是K,如果大于K,则再Partition左边的数组;如果小于K,则Partition右边的数组;如果等于K,恭喜输出结果。
时间复杂度最坏是O(N^2),但因为是无序数组,所以每一次排序平均值都是在数组的中间(N/2)的位置,因此复杂度是O(N + N/2) = O(N)。分析完毕。
Partition应用3:红白蓝三球划分问题
题目意思是给定红白蓝三种不同颜色的球,要求按照指定的颜色顺序对球进行划分,相同颜色的球在一起。抽象成算法问题,其实就是给定一个target,数组中每一个元素都和target进行比较,大于target的在左边,小于target的在右边,等于target的在中间。采用Partition的思想,其实就是比之前多了一种等于target的情况出现,要把等于target的也区分出来。
因此可以定义两个Partition指针,一个从数组头开始向右移动,另一个从数组尾部向左移动;接下来定义一个从左到右的遍历指针,每当扫描发现符合元素小于target,则交换当前位置和左指针的位置,左指针右移;元素大于target,交换当前位置和右指针的位置,右指针左移。当左右指针重叠,则数组元素排列完毕,输出排列后的数组。
代码如下:
// Assume target is in the arr.
void three_way_partition(vector<int> &arr, int target){
int next_less_pos = 0, next_bigger_pos = arr.size()-1;
int next_scan_pos = 0;
while (next_scan_pos <= next_bigger_pos){
if(arr[next_scan_pos] < target){
swap(arr[next_scan_pos++], arr[next_less_pos++]);
}
else if(arr[next_scan_pos] > target){
swap(arr[next_scan_pos], arr[next_bigger_pos--]);
}
else{
next_scan_pos++;
}
}
}
至此,partition的算法详解 + 代码 + 应用已经讲解完毕!
参考:
被忽视的 partition 算法 https://selfboot.cn/2016/09/01/lost_partition/
Partition单向游走和双向游走算法复杂度比较 https://cs.stackexchange.com/questions/11458/quicksort-partitioning-hoare-vs-lomuto/11550