1.二分Partition
二分Partition算法是指在O(n)的时间复杂度和O(1)的空间复杂度的情况下将一个数组分为大于某个数和小于某个数的两部分。快速排序其实就是分治+Partition算法。
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;
}
在cpp的stl中的算法库提供了这个算法的api:
std::nth_element(container, begin, end);
2.第K大的数
通过最基本的Partition算法,我们可以引用于求数组的第K大的数,比如中位数。我们只需要不断分成两部分,然后将多的那部分继续分成两部分。直到两边一样多。
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;
}
3.三分Partition
有的时候数组中大小关系并不只是大于和小于,还有等于。所以如果我们想将一个数组划分为小于x,等于x,大于x的三部分。那么我们可以对二分Partition进行简单的改造就可以实现上面的需求了。
int partition(vector<int> &nums, int begin, int end)
{
int pivot = nums[begin];
int pos1 = begin, pos2 = end;
for (int i = begin + 1; i <= pos2; ++i)
{
if (nums[i] < pivot)
swap(nums[pos1++], nums[i]);
else if (nums[i] > pivot)
swap(nums[pos2--], nums[i--]);
}
return pos1;
}
当然也可以使用二分Partition+三向切分来解决(荷兰国旗问题), 三向切分的作用就是根据目标值分为大于,等于,小于三个部分。
void wiggleSort(vector<int>& nums)
{
int n = nums.size();
std::nth_element(nums.begin(), nums.begin() + n/2, nums.end());
int mid = nums[n/2];
// 3-way-partion
int i = 0, j = 0, k = n - 1;
while (j <= k) {
if (nums[j] > mid) {
swap(nums[i++], nums[j++]);
} else if (num[j] < mid) {
swap(nums[j], nums[k--]);
} else {
++j;
}
}
}