1.快速排序的思想
经过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录比关键字大的,一部分记录比关键字小的,在可分别对这两部分记录继续进行排序,以达到整体有序的目的。
<pre name="code" class="cpp">void Qsort(int *ar, int left, int right); //函数声明
int partition(int *arr, int left, int right);//函数声明
/
void quick_sort(int *arr, int len) //排错
{
assert(arr != NULL);
Qsort(arr, 0, len - 1);
}
void Qsort(int *arr, int left, int right) //左右来筛
{
int mid = 0;
if (left < right)
{
mid = partition(arr, left, right);//一次划分
Qsort(arr, left, mid - 1);
Qsort(arr, mid + 1, right);
}
}
int partition(int *arr, int left, int right)
{
int key = arr[right];//注意和下面左筛右筛对应
while (left < right)
{
while (left < right && arr[left] <= key) //左筛
{
left++;
}
if (left < right) swap(arr, left, right); //交换
while (left < right && arr[right] >= key)//右筛
{
right--;
}
if (left < right) swap(arr, left, right); //交换
}
return left;
}
///
void swap(int *arr, int left, int right)
{
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
partition函数要做的事情,就是先选取其中的一个关键字,然后想办法将它放到一个位置,使得它左边的值都比它小,右边的值都比它大,我们称这样的关键字为枢纽。pivot
2.空间时间
时间: 最优情况O(nlogn) 最坏情况0(n^2)
空间: 最好情况O(log2n) 最坏情况O(n) 平均情况 空间O(logn);
稳定: 不稳定
3.快速排序的优化:
3.1优化选取枢轴: 随机取中法。
三数取中法。(取三个关键字先进行排序,将中间数作为枢轴,一般是取左端、右端和中间三个数。
int pivotkey;
int m=low + (high-low)/2;
if(ar[low] > ar[high) swap(ar,low,high);
if(ar[m] > ar[high]) swap(ar,high,m);
if(ar[m] > ar[low]) swap(ar,m,low);
pivotkey = ar[low];
3.2优化不必要的交换
3.3优化小数组时的排序方案
3.4优化递归操作。
/
/**使用递归快速排序**/
template<typename Comparable>
void quicksort1(vector<Comparable> &vec, int low, int high)
{
if (vec.empty() || low < 0) return;
if (low < high)
{
int mid = partition(vec, low, high);
quicksort1(vec, low, mid - 1);
quicksort1(vec, mid + 1, high);
}
}
/**使用栈的非递归快速排序**/
<pre name="code" class="cpp">/**使用栈的非递归快速排序**/
template<typename Comparable>
void quicksort2(vector<Comparable> &vec, int low, int high)
{
stack<int> st;
if (low < high)
{
int mid = partition(vec, low, high);
if (low < mid-1)
{
st.push(low);
st.push(mid-1);
}
if (mid+1 < high)
{
st.push(mid+1);
st.push(high);
}
//其实就是用栈保存每一个待排序子串的首尾元素下标,下一次while
//循环时取出这个范围,对这段子序列进行partition操作
while (!st.empty())
{
int high = st.top(); //范围的右边
st.pop();
int low = st.top(); //范围的左边
st.pop();
mid = partition(vec, low, high);
if (low < mid - 1)
{
st.push(low);
st.push(mid-1);
}
if (mid + 1< high)
{
st.push(mid+1);
st.push(low);
}
}
}
}
//一次划分
template <typename Comparable>
int partition(vector<Comparable> &vec, int low, int high)
{
Comparable pivot = vec[low]; //任选元素作为轴,这里选首元素
while (low < high)
{
while (low < high && vec[high] >= pivot) high--;
if (low < high) vec[low] = vec[high];
while (low < high && vec[low] <= pivot) low++;
if (low < high) vec[high] = vec[low];
}
vec[low] = pivot; //此时low==high
return low;
}
可以看到非递归的算法比递归实现还要慢。下面解释为什么会这样。
递归算法使用的栈由程序自动产生,栈中包含:函数调用时的参数和函数中的局部变量。如果局部变量很多或者函数内部又调用了其他函数,则栈会很大。每次递归调用都要操作很大的栈,效率自然会下降。
而对于非递归算法,每次循环使用自己预先创建的栈,因此不管程序复杂度如何,都不会影响程序效率。
但对于上面的快速排序,由于局部变量只有一个mid,栈很小,所以效率并不比非递归实现的低。
//
对长度为n 的线性表作快速排序,在最坏情况下,比较次数为n(n-1)/2.
快速排序的最坏情况就是数组有序,选的基数要和每一个数进行比较,(n-1)+(n-2)+...+1=n(n-1)/2次。