Partition算法及其应用
Partition算法及其应用
本文记录partition算法的两种实现方案,及其经典应用场景
Partition算法
partition算法的作用是选定一个元素pivot,然后将元素划分为两个部分,小于pivot的放在左边,大于pivot的放在右边。
实现上,可以分为单向扫描和双向扫描两种方案。
单向扫描算法:
单向扫描,是利用一个指针扫描数组,凡是小于pivot的都放到数组的左端,最后把把pivot放回中间位置。
int partition(vector<int> &nums, int begin, int end)
{
int pivot = nums[begin];
int pos = begin;
for(int i = begin+1; i < end; ++i) {
if(nums[i] <= pivot) {
swap(nums[++pos],nums[i]);
}
}
swap(nums[pos], nums[begin]);
return pos;
}
双向扫描算法:
双向扫描因为交换次数更少,效率更高一些。使用两个指针分别从头部和尾部进行扫描,头部遇到的大于pivot的元素和尾部遇到的小于pivot的元素进行交换。
int partition(vector<int> &nums, int begin, int end)
{
int pivot = nums[begin];
while(begin < end) {
while(begin < end && nums[--end] >= pivot);
nums[begin] = nums[end];
while(begin < end && nums[++begin] <= pivot);
nums[end] = nums[begin];
}
nums[begin] = pivot;
return begin;
}
不过我更习惯这种闭区间写法:
int partition(int a[], int l, int r)
{
int x = a[l];
int i = l;
int j = r;
while (i < j) {
while (i < j && a[j] >= x) j--;
if (i < j) a[i++] = a[j];
while (i < j && a[i] < x) i++;
if (i < j) a[j--] = a[i];
}
a[i] = x;
return i;
}
注意事项
无论是开区间还是闭区间,都是 while(i < j)
if (i < j)
,这一点与二分查找不同:开区间 while (l < r)
闭区间 while (l <= r)
。
应用
快速排序算法:
经典快速排序。
void quickSort(vector<int> &nums, int begin, int end)
{
if(end - begin <= 1) return;
int mid = partition(nums, begin, end);
quickSort(nums, begin, mid);
quickSort(nums, mid, end);
}
快速选择算法:
选择数组中的第k大元素 LeetCode 215. Kth Largest Element in an Array。
int findKthLargest(vector<int> &nums, int k)
{
int len = nums.size();
int res = 0;
int left = 0;
int right = len;
while(left < right) {
int pos = partition(nums, left, right);
if(pos == len-k) {
res = nums[pos];
break;
} else if(pos < len-k) {
left = pos+1;
} else {
right = pos;
}
}
return res;
}