复习数据结构:排序算法(五)——快速排序的各种版本

之前已经比较熟悉快排的基本思想了,其实现的方式也有很多种。下面我们罗列一些常见的实现方式:

版本一:算法导论上的单向扫描,选取最后一个元素作为主元

#include<iostream>
using namespace std; 

int partition(int data[], int low, int high)
{
	int pivot = data[high];  // let the last atom be the pivot
	int i = low - 1;  // mark the pivot's true index: i+1
	for(int j = low; j < high; j++)  // 数组是从low ~ high, 这里就要到high-1截止
	{
		if(data[j] <= pivot)
		{
			i++;  // 说明pivot的位置要后移一位
			swap(data[i], data[j]);  // 较小的数放前面,由于pivot是i+1, 所以把data[j]放在i的位置
		}
	}

	swap(data[i+1], data[j]);  // 把主元放在合适的位置i+1,记住不是pivot,而是data[j]

	return i+1;  // return the true index of pivot, that is, i+1
}

void quickSort(int data[], int low, int high)
{
	if(low < high)
	{
		int k = partition(data, low, high);
		quickSort(data, low, k-1);
		quickSort(data, k+1, high);
	}
}

int main()
{
	int data[] = {2, 6, 3, 8, 1, 4};
	quickSort(data, 0, 5); 

	for(int i = 0; i < 6; i++)
		cout<<data[i]<<' ';
	cout<<endl; 

	return 0;
}

版本二:双向扫描,选取第一个元素作为主元

#include<iostream>
using namespace std; 

void print(int a[], int n){
    for(int j= 0; j<n; j++){
        cout<<a[j] <<"  ";
    }
    cout<<endl;
}  

void swap(int *a, int *b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}  

// 双向扫描法
int partition(int a[], int low, int high)
{
    int privotKey = a[low];                             //基准元素
    while(low < high){                                   //从表的两端交替地向中间扫描
        while(low < high  && a[high] >= privotKey) --high;  //从high 所指位置向前搜索,至多到low+1 位置。将比基准元素小的交换到低端
        swap(&a[low], &a[high]);
        while(low < high  && a[low] <= privotKey ) ++low;
        swap(&a[low], &a[high]);
    }
    print(a,10);
    return low;
}  

void quickSort(int a[], int low, int high){
    if(low < high){
        int privotLoc = partition(a,  low,  high);  //将表一分为二
        quickSort(a,  low,  privotLoc -1);          //递归对低子表递归排序
        quickSort(a,   privotLoc + 1, high);        //递归对高子表递归排序
    }
}  

int main(){
    int a[10] = {3,1,5,7,2,4,9,6,10,8};
    cout<<"初始值:";
    print(a,10);
    quickSort(a,0,9);
    cout<<"结果:";
    print(a,10);
	return 0;
}  

版本三:随机选取一个元素作为主元

//随机选择主元的快排主要步骤是先随机选择主元的位置,然后把该主元与第一个元素调换,之后的步骤与拿第一个元素当做主元的算法一样

#include<iostream>
using namespace std; 

//交换两个元素值,咱们换一种方式,采取引用“&”
void swap(int& a , int& b)
{
 int temp = a;
 a = b;
 b = temp;
}

//返回属于[lo,hi)的随机整数
int rand(int lo,int hi)
{
 int size = hi-lo+1;
 return  lo+ rand()%size;
}

//分割,换一种方式,采取指针a指向数组中第一个元素
int RandPartition(int* data, int lo , int hi)
{
 //普通的分割方法和随机化分割方法的区别就在于下面三行
 swap(data[rand(lo,hi)], data[lo]);
    int key = data[lo];
 int i = lo;

    for(int j=lo+1; j<=hi; j++)
 {
  if(data[j]<=key)
  {
   i = i+1;
   swap(data[i], data[j]);
  }
 }
 swap(data[i],data[lo]);
 return i;
}

//逐步分割排序
void RandQuickSortMid(int* data, int lo, int hi)
{
 if(lo<hi)
 {
  int k = RandPartition(data,lo,hi);
  RandQuickSortMid(data,lo,k-1);
  RandQuickSortMid(data,k+1,hi);
 }
}
int main()
{
 const int N = 100; //此就是上文说所的“极限”测试。为了保证程序的准确无误,你也可以让N=10000。
 int *data = new int[N];
    for(int i =0; i<N; i++)
  data[i] = rand();   //同样,随机化的版本,采取随机输入。
    for(i=0; i<N; i++)
  cout<<data[i]<<" ";
    RandQuickSortMid(data,0,N-1);
 cout<<endl;
 for(i=0; i<N; i++)
  cout<<data[i]<<" ";
 cout<<endl;
    return 0;
}

版本四:三数取中作为主元

上面的程序版本,其中算法导论上采取单向扫描中,是以最后一个元素为枢纽元素,即主元,而在Hoare版本及其几个变形中,都是以第一个元素、或中间元素为主元,最后,上述给的快速排序算法的随机化版本,则是以序列中任一一个元素作为主元。

那么,枢纽元素的选取,即主元元素的选取是否决定快速排序最终的效率列?

答案是肯定的,当我们采取data[lo],data[mid],data[hi]三者之中的那个第二大的元素为主元时,便能尽最大限度保证快速排序算法不会出现O(N^2)的最坏情况。这就是所谓的三数取中分割方法。当然,针对的还是那个Partition过程。

//三数取中分割方法
int RandPartition(int* a, int p , int q)
{
 //三数取中方法的关键就在于下述六行,可以得到a[p], a[m], a[q]三个数中的中位数
 int m=(p+q)/2;
 if(a[p]<a[m])
  swap(a[p],a[m]);
 if(a[q]<a[m])
  swap(a[q],a[m]);
 if(a[q]<a[p])
  swap(a[q],a[p]);
 int key = a[p];
 int i = p;

 for(int j = p+1; j <= q; j++)
 {
  if(a[j] <= key)
  {
   i = i+1;
   if(i != j)
    swap(a[i], a[j]);
  }
 } 

 swap(a[i],a[p]);
 return i;
}

void QuickSort(int data[], int lo, int hi)
{
    if (lo<hi)
    {
        int k = RandPartition(data, lo, hi);
        QuickSort(data, lo, k-1);
        QuickSort(data, k+1, hi);
    }
}

版本五:非递归版本

 //递归的本质是什么?对了,就是借助栈,进栈,出栈来实现的。
   if( (key-lo)<(key-hi) )
   {
    st.push(key+1);
    st.push(hi);
    hi=key-1;
   }
   else
   {
    st.push(lo);
    st.push(key-1);
    lo=key+1;
   }
  }
  if(st.empty())
   return;
  hi=st.top();
  st.pop();
  lo=st.top();
  st.pop();
 }while(1);
}

参考:

http://blog.csdn.net/hguisu/article/details/7776068

http://blog.csdn.net/xiazdong/article/details/8462393

http://blog.csdn.net/v_JULY_v/article/details/6262915

时间: 2024-10-13 01:14:21

复习数据结构:排序算法(五)——快速排序的各种版本的相关文章

排序算法之快速排序的随机化版本

4.3 快速排序的随机化版本 这种方法并不是一种全新的排序算法,而是在快速排序的基础上加入随机化的因素,因素,因而仍然将其作为第四种方法(快速排序)的一种补充. 为什么要提出快速排序的随机化版本,主要是对于快速排序法其划分情况的好坏会直接影响排序的效率,而且,快速排序的平均性能较好,所以,加入随机化成分,可以使该算法对于所有输入均能获得较好的平均情况性能. 针对加入随机化的环节,选取的是选取主元的环节,因为主元的选取直接影响快速排序算法的数组划分. C代码实现为: #include <stdio

数据结构排序算法之快速排序

排序算法包括很多种,其中快速排序是其中一种比较快的排序算法,今天就来介绍一下: 快速排序的基本实现思想就是将当前待排序列分成两个部分.一个值.一个值:就是选定出一个值作为被比较的元素.两个部分:所有比该被选定元素大的部分都去该元素的右边,所有比被选定元素小的部分都去该元素的左边.这样我们就确定了该元素在这个待排序列中的位置,其实也就是我们已经将这个元素“排好了”. 那么,怎么才能完成一次的快速排序呢? 我们选定一个被比较的元素,一般都是选第一个,即数组中第一个元素作为val,然后我们给出两个指针

排序算法 之 快速排序

快速排序是基于分治思想的一种排序算法,就像该方法的名字一样,速度比较快,所以叫做快速排序:它的平均时间复杂度为O(N*logN),最坏时间复杂度为O(n2),由于快速排序在序列元素数量多的时候速度比较快,所以很多语言内置的排序方法也是用快速排序实现的.快速排序也有很多优化的版本,比如在排序时基数的选择等等-下面就说一下一般的快速排序的实现. 基本思想: 快速排序的基本思想就是,先从待排序的序列中任选一个元素作为基数,然后将序列中的其他小于基数的元素放在基数的左边,大于或等于基数的元素放在基数的右

python数据结构与算法 35 快速排序

快速排序 快速排序也使用了分而治之的策略来提高性能,而且不需要额外的内存,但是这么做的代价就是,列表不是对半切分的,因而,性能上就有所下降. 快速排序选择一个数值,一般称为"轴点",虽然有很多选取轴点的方法,我们还是简单地把列表中第一个元素做为轴点了.轴点的作用是帮助把列表分为两个部分.列表完成后,轴点所在的位置叫做"切分点",从这一点上把列表分成两部分供后续调用. 图12所示,54将作为轴点.这个例子我们已经排过多次了,我们知道54在排好序后将处于现在31的位置上

黑马程序员——数据结构排序算法总结

-----------android培训.java培训.java学习型技术博客.期待与您交流!------------ 下面是几个网上常见的总结图: 有些不同之处:集中在希尔排序的时间复杂度.快速归并的空间复杂度上 个人总结口诀: 选择N方-->选择排序的最好最坏平均都N方 插入冒泡最好N-->插入冒泡的最好是N,其他是N方 归并堆n乘logN-->归并希尔堆的最好最坏平均都是n乘logN 快速最坏N方-->快速排序的最坏是N方,其他是n乘logN 快速选择希尔堆不稳定-->

数据结构排序算法Java实现

闲的无聊又拿起了大学的数据结构的书看起来 <数据结构(c语言版)>严蔚敏 吴伟民编著. 里面有几个排序算法,感觉挺好玩的,就想着写出来玩玩. package test.sort; /** * @Title: TODO * @Description: TODO * @author: * @date: 2014-8-10 上午11:20:43 * */ public class quickSort { private static int datas[] = {23,42,12,45,56,63,

常用排序算法之——快速排序(C语言+VC6.0平台)

经典排序算法中快速排序具有较好的效率,但其实现思路相对较难理解. #include<stdio.h> int partition(int num[],int low,int high) //以key为基准 将待排数列“高”.“低 ”两部分,“高”部分的所有数据比key大,“低”部分的数据都比key小 { int left,right,key; left=low;right=high;key=num[low]; while(left<right) { while(left<right

常用排序算法之——快速排序

快速排序的原理: 首先找一个标兵值,等于某一个元素值:遍历数组,将数组分为小于标兵值和大于标兵值的两部分:然后分别对两个部分采用快速排序,递归. 分开数组时,维持一个指针,指向已找到小部分的最后一个元素:一个指针用于遍历. 不稳定排序算法.当数组已经有序时,时间复杂度最差,为O(N2),平均.最优情况下都为O(N lgN). 代码如下: 1 #include <iostream> 2 using namespace std; 3 4 template<typename T> 5 v

排序算法系列——快速排序

记录学习点滴 快速排序算法是一种很有趣的算法,短小精悍,性能强劲,对于大部分情况都可以胜任,但对极端环境难以应付. 快速排序我理解为:这是一个“以自我为中心的”+“分治法”思想的算法. 分治法不必多说,化繁为简,那就是逐个击破. 那什么是“以自我为中心”?顾名思义,就是每次都一个“我”,每个人都要围绕“我”行事,比“我”小的都去左边站着,比“我”他大的都去右边站着,而且“我”不去关心每一边都有谁,反正你没“我”大或者小就行.一旦“我”落位了妥帖了,“我”就不动了.然后再在左右两边分别产生新“我”

排序算法之快速排序Java实现

排序算法之快速排序 舞蹈演示排序: 冒泡排序: http://t.cn/hrf58M 希尔排序:http://t.cn/hrosvb  选择排序:http://t.cn/hros6e  插入排序:http://t.cn/hros0W  快速排序:http://t.cn/ScTA1d  归并排序:http://t.cn/Sc1cGZ 快速排序是一个就地排序,分而治之,大规模递归的算法.从本质上来说,它是归并排序的就地版本. 1.快速排序可以由下面四步组成:(1) 如果不多于1个数据,直接返回.(2