思想
分治。选一个pivot,将比pivot小的元素放在左边,比pivot大的元素放在右边,对左右两个子数组递归调用QuickSort。
实现
int randomNum(int x, int y) {
srand(time(0)); // use system time as seed
return x + rand() % (y - x + 1);
}
int partition(vector<int>& nums, int l, int r) {
int pivotIndex = randomNum(l, r);
swap(nums[pivotIndex], nums[r]);
int smaller = l - 1;
for (int i = l; i < r; ++i) {
if (nums[i] <= nums[r]) {
++smaller;
swap(nums[smaller], nums[i]);
}
}
++smaller;
swap(nums[smaller], nums[r]);
return smaller;
}
void QuickSort(vector<int>& nums, int low, int high) {
if(low < high) {
int pivotPos = partition(nums, low, high);
QuickSort(nums, low, pivotPos - 1);
QuickSort(nums, pivotPos + 1, high);
}
}
int partition(vector<int>& num, int low, int high) {
int pivot = num[low];
while (low < high) {
while (low < high && num[high] >= pivot) {
--high;
}
num[low] = num[high];
while (low < high && num[low] <= pivot) {
++low;
}
num[high] = num[low];
}
num[low] = pivot;
return low;
}
void qSort(vector<int>& num, int low, int high) {
if (low < high) {
int pivot = partition(num, low, high);
qSort(num, low, pivot - 1);
qSort(num, pivot + 1, high);
}
}
// 非递归,用栈保存要操作的范围的下标
void qSortNotR(vector<int>& num, int low, int high) {
stack<int> s;
s.push(low);
s.push(high);
while (!s.empty()) {
int r = s.top();
s.pop();
int l = s.top();
s.pop();
int pivotPos = partition(num, l, r);
if (l < pivotPos - 1) {
s.push(l);
s.push(pivotPos - 1);
}
if (pivotPos + 1 < r) {
s.push(pivotPos + 1);
s.push(r);
}
}
}
性能
划分的两个子问题规模分别为0和n时,时间复杂度最坏为(O(n^2));
两个子问题规模相同时,时间复杂度最好为(O(nlgn)),平均性能更接近最好情况。
假设输入数据的所有排列不是等概率的,那么为了避免最坏情况的发生,选择pivot可以随机进行,这样被pivot分开的两个子问题规模会接近(n/2)。
复杂度的证明既可以用主定理,也可以自己推导,在最优情况下:
[T(n)=2T(n/2)+n \
T(n)=2(2T(n/4)+n/2)+n=4T(n/4)+2n \
T(n)=4(2T(n/8)+n/4)+2n=8T(n/8)+3n \
... \
T(n)=nT(1)+nlogn=n+nlogn
]