快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法。
其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
如何寻找基准值?
一般情况下会选择排序序列的最后一个元素作为基准值,但是如果待排序列的最后一个元素是最值或者接近于最值的情况下,会使得排序后的左右序列元素个数相差过大,降低了效率,所以我们一般采用三数取中法:找出序列的开始节点、结束节点以及序列的中的节点取中间值作为基准值。
将区间按照基准值划分为左右两半部分的常见方式有:
hoare版本 :
1、首先设定基准值
假设我们找到了基准值是最后一个值,此时我们设定其位置为 pivot ,同时设定两个指针 left 、right 分别指向待排序列的左右两端;
2、然后开始移动 left(++) 和 right(--)
当 left 所指向的数字比 pivot 所指向的数字大时,停止移动
当 right 所指向的数字比 pivot 所指向的数字小时,停止移动
当 left 和 right 都停止移动后,交换两个指针所指向的数字;
3、继续移动,当 left 和 right 想遇时停止移动,这里我们假设 left 先移动,那么两者的可以相遇的情况一定是 左右都已经排好了,并且此时 left 和 right 所指向的数字一定比 pvoit 所指向的数字大!!!
此时只需要将,left 和 right 所指向的数组和 pvoit 所指向的数字进行交换即可;
4、此时以及成功将待排序列分为左右两部分 (以 6 为中心),之后只需要对左右序列进行以上同的工作就可以了;
用递归实现是非常简单的,主要是要完成一次排序后,不断对左右生于的子序列进行递归即可。
那么,非递归又怎么办?其实也很简单,利用栈(stack)的结构,来模拟进行递归就可以了,函数的递归调用本来也就是在函数调用栈中进行的~
代码(C++)如下所示:
template <class T>
void Swap(T* a, T* b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
int partition(int* data, int left, int right)
{
int pivot = right;
while (left < right) {
while (left < right && data[left] <= data[pivot])
++left;
while (left < right && data[right] >= data[pivot])
--right;
Swap(&data[left], &data[right]);
}
Swap(&data[left], &data[pivot]);
return pivot;
}
//递归
void quickSortRecusive(int* data, int left, int right){
if (data == NULL || left < 0 || right <= 0 || left > right)
return;
int pivot = partition(data,left,right);
if (pivot > left)
quickSortRecusive(data, left, pivot - 1);
if (pivot < right)
quickSortRecusive(data, pivot + 1, right);
}
//非递归实现
void quickSortNorecusive(int* data, int left, int right)
{
if (data == NULL || left < 0 || right <= 0 || left > right)
return;
stack<int> s;
int l, r;
s.push(right);
s.push(left);
while (!s.empty()) {
l = s.top();
s.pop();
r = s.top();
s.pop();
if (l < r) {
int povit = partition(data, l, r);
if (povit > l) {
s.push(povit - 1);
s.push(l);
}
if (r > povit) {
s.push(r);
s.push(povit + 1);
}
}
}
}
挖坑法 :
挖坑法顾名思义,我们需要把选择的基准值的位置固定下来,就像于是挖了一个坑。
1、首先需要定一个指针 index 指向选定的基准值而且记住这个值 pivot,并且需要定义两个指针 left 和 right 分别指向 代拍序列的首段和尾端;
2、移动指针及判断,假设我们打算先移动 right,首先要判断 right 所指向的值 和 index 所指向的 pivot 相比较
如果 right 的值小于 index 的值,那就把right 所指向的值,填到 left 中,并且将 index 指向right,left 向右移动;
3、然后比较 left 和 index 所指向的值:
如果 left 的值 大于 index 的值,就将 left 的值 填入 indx 中,nidex 指向 left,right 左移;
4、继续移动,当 left 和 right 相遇的时候,只需要将开始记录的 Pivot 的值,填入到当前 index 和 left right 共同指向的位置即可;
代码(C++)如下:
int partitionW(int* arr,int left,int right) {
int pivot = arr[left];
int index = left;
while (left < right) {
while (right != index) {
if (arr[right] < pivot) {
arr[index] = arr[right];
index = right;
++left;
}
else
--right;
}
while (left != index) {
if (arr[left] > pivot) {
arr[index] = arr[left];
index = left;
--right;
}
else
++left;
}
}
arr[index] = pivot;
return index;
}
//递归
void quickSortRecusiveW(int* data,int left,int right) {
if (data == NULL || left < 0 || right <= 0 || left >right)
return;
int pos = partitionW(data, left, right);
//左右
quickSortRecusiveW(data, left, pos - 1);
quickSortRecusiveW(data, pos + 1, right);
}
//非递归
void quickSortNorecusiveW(int* data, int left, int right) {
if (data == NULL || left < 0 || right <= 0 || left > right)
return;
stack<int> s;
int l, r;
s.push(right);
s.push(left);
while (!s.empty()) {
l = s.top();
s.pop();
r = s.top();
s.pop();
if (l < r) {
int pos = partitionW(data, l, r);
if (l < pos) {
s.push(pos - 1);
s.push(l);
}
if (r > pos) {
s.push(r);
s.push(pos + 1);
}
}
}
}
前后指针版本:
1、首先我们选择基准值设为 piovt ,并且设定两个指针 left 和 right 分别指向待排序列的初始位置和结尾位置。
2、首先移动 right 指针,如果 right 所指向的值小于 piovt ,则停止移动。
然后移动 left 指针,如果 left 所指向的值大于 piovt,则停止移动;
3、交换 left 的值和 right 的值;
4、继续上面的动作,直到 left 和 right 指向同一个值,此时只需要将 pivot 的值和这个交换即可;
int partitionZ(int* arr, int left, int right) {
int index = left;
int pivot = arr[left];
while (left < right) {
while (left < right) {
if (arr[right] < pivot)
break;
--right;
}
while (left < right) {
if (arr[left] > pivot)
break;
++left;
}
if(left < right)
swap(arr[left], arr[right]);
}
swap(arr[left], arr[index]);
return left;
}
//递归
void quickSortRecusiveZ(int* arr, int left, int right) {
if (arr == NULL || left < 0 || right <= 0 || left > right)
return;
int pos = partitionZ(arr, left, right);
if(left < pos)
quickSortRecusiveZ(arr, left, pos - 1);
if(right > pos)
quickSortRecusiveZ(arr, pos + 1, right);
}
//非递归
void quickSortNorecusiveZ(int* arr, int left, int right) {
if (arr == NULL || left < 0 || right <= 0 || left > right)
return;
stack<int> s;
s.push(right);
s.push(left);
int l, r;
while (!s.empty()) {
l = s.top();
s.pop();
r = s.top();
s.pop();
if(l < r){
int pos = partitionZ(arr, l, r);
if (l < pos) {
s.push(pos - 1 );
s.push(l);
}
if (r > pos) {
s.push(r);
s.push(l + 1);
}
}
}
}