您的位置:首页 > 其它

排序总结系列五:快速排序

2016-10-06 10:19 239 查看
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次。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息