排序算法之快速排序的多种版本

来源:互联网 发布:网狐6603经典版源码 编辑:程序博客网 时间:2024/05/20 09:43

快速排序

    快速排序相对于其他的排序算法是较为快速的一种排序算法,主要体现在它的时间复杂度上.它的主要思想是:先选取一个值做关键字,通过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录均比关键字小,另一部分记录均比关键字大,则可分别对这两部分记录继续进行排序,以达到整个有序的目的.

    因此快速排序中最重要的就是划分区间,在下文的实现中我将介绍三种类型的单趟排序.

 一.左右指针法

    先找到一个值做关键字(可以选择最后一个元素也可以选择第一个元素),在我的代码实现中我选取的是最后一个元素做关键字,从左边开始找比关键字大的元素,从右边开始找比关键字小的元素,将左右找到满足题意的值交换,当左右相遇时说明一趟排序已经结束,此时需要将相遇点与关键字交换.

   

int PastSortA(int *a,int start,int end){int left=start;int right=end;int ret=GetMid(a,start,end);swap(a[ret],a[end]);int pivotkey=a[end];    //最后一个做关键字while (left < right){//left找比pivotkey大的数//right找比pivotkey小的数while (left < right && a[left] <= pivotkey){++left;}while (left < right && a[right] >= pivotkey){--right;}if(left < right)swap(a[left],a[right]);}swap(a[right],a[end]);return right;}


 

 

 二.挖坑法

   挖坑法是左右指针法的一种变形,只不过是从左边找到比关键字大的元素时与关键字交换,从右边找到比关键字小的元素与关键字交换.

   

int PastSortB(int *a,int start,int end){int left=start;int right=end;int ret=GetMid(a,start,end);swap(a[ret],a[end]);int pivotkey=a[end];while (left < right){while (left < right && a[left] < pivotkey){++left;}swap(a[left],a[right]);while (left < right && a[right] > pivotkey){--right;}swap(a[right],a[left]);}a[left]=pivotkey;return left;}


 

 

三.前后指针法

   开始时让prev指向start的前一个位置,cur指向start位置,从左向右,当cur找到指向的值小于关键字,将prev后移,如果cur和prev没有指向同一个位置,然后将prev所指向的值与cur所指向的值进行交换,最后交换prev和end指向的值.

  

int PastSortC(int *a,int start,int end){int ret=GetMid(a,start,end);swap(a[ret],a[end]);int pivotkey=a[end];int prev=start-1;int cur=start;while (cur < end){if (a[cur] < pivotkey && ++prev != cur){swap(a[prev],a[cur]);}++cur;}swap(a[++prev],a[end]);return prev;}


 

 

快速排序的优化

   1).三数取中:在代码实现中GetMid()中就实现了在中间值,第一个值,最后一个值选取中间一个值,值得注意的是在这三个数选取中间值后将这个中间值放置到最后一个数的位置.

   2).判断元素的个数:当数据较大时用快速排序,当数据较小时直接插入,一般这个值 为13.

  

int GetMid(int *a,int start,int end){int mid=start+((end-start)>>1);if (a[start] < a[end]){if(a[end] < a[mid])return end;else if(a[mid] > a[start])return mid;elsereturn start;}else     //a[start] > a[end]{if(a[end] > a[mid])return end;else if(a[start] < a[mid])return start;elsereturn mid;}}


 

 

 快速排序的递归&非递归

 

void QuickSort(int *a,int start,int end){if (start < end){int size=end-start+1;//数据较大快排if(size > 2){int div=PastSortB(a,start,end);QuickSort(a,start,div-1);QuickSort(a,div+1,end);}//数据较小直接插入else{InsertSort(a,size);}}}void QuickSortR(int *a,int start,int end){stack<int> s;s.push(end);s.push(start);while (!s.empty()){int left=s.top();s.pop();int right=s.top();s.pop();int div=PastSortC(a,left,right);if(left < div-1){s.push(div-1);s.push(left);}if(div+1 < right){s.push(right);s.push(div+1);}}}


 

  

   我们知道,快速排序的时间复杂度取决于它的递归的深度.在最好的情况下,每次的单趟排序都很均匀,它的时间复杂度为O(N*lgN).那么最坏情况下,也就是每次选的枢纽值是(接近)最大值或者最小值,也就是我们给定的待排序的数组是(接近)升序或者逆序,这时我们就需要n-1次递归调用.
   总的比较次数:n-1 + n-2 + n-3 +...+1 = n(n-1)/2,时间复杂度为O(N^2).
   在平均情况下快速排序的时间复杂度是O(N*lgN).
   不管是哪种快速排序的版本,因为它都是跳跃式的交换所以快速排序是一种不稳定的排序算法.






  

    

0 0
原创粉丝点击