original solution
参考算法导论实现的分组算法
int partition(vector<int>& nums, int p, int r) {
int x = nums[r];
int s = p - 1; // the last smaller
for (int k = p; k < r; ++k) {
if (nums[k] <= x){
++s;
swap(nums[s], nums[k]);
}
}
++s;
swap(nums[r], nums[s]);
return s;
}
输入
5 2 4 7 5 1 5 9 5 10 5
分组结果
7
5 2 4 5 1 5 5 5 7 10 9输入
5 5 5 5 5
分组结果
4
5 5 5 5 5
可以看到,第二组输入的分组结果很不均衡,这在会影响快排算法的性能。
improved solution1
在做分组的时候,同时区分大于和小于pivot值的元素。小于pivot值的元素就往左边放,大于pivot值的元素就往右边放,等于pivot的元素放中间。partition结束之后,返回等于pivot值的元素的中间位置。
int partition(vector<int>& nums, int p, int r) {
int x = nums[r];
int s = p - 1; // the last smaller
int cnt = 0; // count # number equals to pivot
for (int k = p; k < r; ++k) {
if (nums[k] < x){
swap(nums[s+cnt+1], nums[k]);
swap(nums[s+cnt+1], nums[s+1]);
++s;
} else if (nums[k] == x) {
swap(nums[s+cnt+1], nums[k]);
swap(nums[s+cnt+1], nums[s+1]);
++cnt;
}
}
swap(nums[r], nums[s+cnt+1]);
return s+1+cnt/2;
}
输入
5 2 4 7 5 1 5 9 5 10 5
分组结果
5
2 4 1 5 5 5 5 5 7 10 9输入
5 5 5 5 5
分组结果
2
5 5 5 5 5
改进的代码的均衡性就好多了。
improved solution2
把较小的数往左边放,较大的数放右边。
// partition array, return the index of pivot
int partition(vector<int>& nums, int p, int r) {
int x = nums[r];
int s = p - 1; // the last smaller
int l = r; // the first larger
for (int k = p; k < l; ++k) {
if (nums[k] > x) {
--l;
swap(nums[l], nums[k]);
--k; // nums[k] is new, should do again
} else if (nums[k] < x){
++s;
swap(nums[s], nums[k]);
}
}
swap(nums[r], nums[l]);
return (s+1+l)/2;
}
测试输入
5 2 4 7 5 1 5 9 5 10 5
分组结果
5
2 4 1 5 5 5 5 5 10 7 9测试输入
5 5 5 5 5
分组结果
2
5 5 5 5 5