排序总结系列五:快速排序
2016-10-06 10:19
239 查看
1.快速排序的思想
经过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录比关键字大的,一部分记录比关键字小的,在可分别对这两部分记录继续进行排序,以达到整体有序的目的。
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优化递归操作。
/////////////////////////////////////////////////////////////////////////////////////////////////
/**使用递归快速排序**/
/**使用栈的非递归快速排序**/
可以看到非递归的算法比递归实现还要慢。下面解释为什么会这样。
递归算法使用的栈由程序自动产生,栈中包含:函数调用时的参数和函数中的局部变量。如果局部变量很多或者函数内部又调用了其他函数,则栈会很大。每次递归调用都要操作很大的栈,效率自然会下降。
而对于非递归算法,每次循环使用自己预先创建的栈,因此不管程序复杂度如何,都不会影响程序效率。
但对于上面的快速排序,由于局部变量只有一个mid,栈很小,所以效率并不比非递归实现的低。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
对长度为n 的线性表作快速排序,在最坏情况下,比较次数为n(n-1)/2.
快速排序的最坏情况就是数组有序,选的基数要和每一个数进行比较,(n-1)+(n-2)+...+1=n(n-1)/2次。
经过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录比关键字大的,一部分记录比关键字小的,在可分别对这两部分记录继续进行排序,以达到整体有序的目的。
<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次。
相关文章推荐
- 【算法总结系列-1】快速排序-c语言实现
- MPI并行编程系列二:快速排序
- 白话经典算法系列之六 快速排序 快速搞定
- 白话经典算法系列之六 快速排序 快速搞定
- 排序序列排序算法总结(二)——快速排序、归并排序
- 排序算法总结(二)——快速排序、归并排序
- 内排序算法总结——快速排序
- 数据结构排序系列详解之四 快速排序
- 白话经典算法系列之六 快速排序 快速搞定
- 排序总结(冒泡、选择、插入、快速)
- 排序系列--快速排序
- 白话经典算法系列之六 快速排序 快速搞定
- 冒泡,插入,折半插入,希尔,快速,简单选择排序的源代码总结
- 【排序算法】快速排序超级总结
- 【转载】白话经典算法系列之六 快速排序 快速搞定
- 白话经典算法系列之六 快速排序 快速搞定
- 算法系列之六:快速排序
- 白话经典算法系列之八 MoreWindows白话经典算法之七大排序总结篇
- 白话经典算法系列之八 MoreWindows白话经典算法之七大排序总结篇
- 八大排序算法:简单插入排序、冒泡排序、希尔排序、快速排序、堆排序、归并排序等总结。