快排介绍
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两个子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后左右子序列重复该过程,直到所有元素都排在相应位置上为止。
效果演示(先来体会一下)
对照基本思想来看效果演示:
那么我们是怎样将其变为第三趟这个结果的呢?
下面我们具体介绍:
将区间按照基准值划分为左右两半部分的方法:(推荐挖坑法)
挖坑法思想:
- 先选取一个基准值,保存下来,那么该基准值的位置就空余出来了,我们将空出来的这个位置叫做坑,然后从左边找大于基准值的数填在坑中,右边找小于基准值的数,循环往复,直到排好。
图画演示(第一趟)
选取基准值的优化
- 试想一下:
万一数组有序或者接近有序,选取值是最大的或者最小的,那么是快排最糟糕的情况:时间复杂度为O(N^2)
最好的情况是每次选取的是自己区间的中位数:时间复杂度为O(N*log2N) - 为了避免最坏的情况出现我们采取三数取中法来规避这一情况。
- 三数取中法介绍:就是将数组开头、中间、结尾这三个数比较取中间的数,作为基准值。
代码展示
快排递归
//三数取中
int GetMidIndex(int* a, int begin, int end)
{
int mid = (begin + end) / 2;
if (a[begin] < a[mid])
{
if (a[mid] < a[end])
return mid;
else if (a[begin]>a[end])
return begin;
else
return end;
}
else//a[begin]>a[mid ]
{
if (a[mid] > a[end])
return mid;
else if (a[begin] < a[end])
return begin;
else
return end;
}
}
//挖坑法
int PartSort(int *a, int begin, int end)
{
int midIndex = GetMidIndex(a, begin, end);
swap(a[midIndex], a[end]);
int key = a[end];
while (begin < end)
{
while (begin < end&&a[begin] <= key)
{
++begin;
}
//左边找到比key大的,填到右边的坑
a[end] = a[begin];
while (begin < end&&a[end ]>= key)
{
--end;
}
//右边找到小的,填在左边的坑
a[begin] = a[end];
}
a[begin] = key;
return begin;
}
void QuickSort(int *a, int left, int right)
{
assert(a);
if (left < right)
{
int div = PartSort(a, left, right);
//[left,div-1]和[div+1,right]
QuickSort(a, left, div - 1);
QuickSort(a, div + 1, right);
}
}
- 因为递归在数据量很大的时候,可能导致栈溢出,所有我们还介绍非递归的写法。
非递归
void QuickSortNonR(int* a, int left, int right)
{
stack<int> st;
st.push(right);
st.push(left);
while (!st.empty())
{
int begin = st.top();
st.pop();
int end = st.top();
st.pop();
//[begin,end]
int div=PartSort(a, begin, end);
//[begin,div-1] div [div+1,end]
if (end>div + 1)
{
st.push(end);
st.push(div + 1);
}
if (begin < div - 1)
{
st.push(div - 1);
st.push(begin);
}
}
}