常用排序算法总结

来源:互联网 发布:地图龟淘宝哪家的好 编辑:程序博客网 时间:2024/06/06 02:09

本文主要内容转载自http://www.cnblogs.com/eniac12/p/5329396.html#s32 和 http://blog.csdn.net/hguisu/article/details/7776068

目录

    1. 冒泡排序   
    2. 选择排序
    3. 插入排序
        3.1 二分插入排序  
        3.2 希尔排序  
    4. 归并排序
    5. 堆排序
    6. 快速排序

  我们通常所说的排序算法往往指的是内部排序算法,即数据记录在内存中进行排序。

  排序算法大体可分为两种:

    一种是比较排序,时间复杂度O(nlogn) ~ O(n^2),主要有:冒泡排序选择排序插入排序归并排序堆排序快速排序等。

    另一种是非比较排序,时间复杂度可以达到O(n),主要有:计数排序基数排序桶排序等。

  这里我们来探讨一下常用的比较排序算法,非比较排序算法将在下一篇文章中介绍。下表给出了常见比较排序算法的性能:

  

  排序算法稳定性:如果Ai = Aj,排序前Ai在Aj之前,排序后Ai还在Aj之前,则称这种排序算法是稳定的。通俗地讲就是保证排序前后两个相等的数的相对顺序不变。

  对于不稳定的排序算法,只要举出一个实例,即可说明它的不稳定性;而对于稳定的排序算法,必须对算法进行分析从而得到稳定的特性。需要注意的是,排序算法是否为稳定的是由具体算法决定的,不稳定的算法在某种条件下可以变为稳定的算法,而稳定的算法在某种条件下也可以变为不稳定的算法。

  例如,对于冒泡排序,原本是稳定的排序算法,如果将记录交换的条件改成A[i] >= A[i + 1],则两个相等的记录就会交换位置,从而变成不稳定的排序算法。

  其次,说一下排序算法稳定性的好处。排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。基数排序就是这样,先按低位排序,逐次按高位排序,低位排序后元素的顺序在高位也相同时是不会改变的。

 

  1. 冒泡排序(Bubble Sort)

  冒泡排序是一种极其简单的排序算法,也是我所学的第一个排序算法。它重复地走访过要排序的元素,依次比较相邻两个元素,如果他们的顺序错误就把他们调换过来,直到没有元素再需要交换,排序完成。这个算法的名字由来是因为越小(或越大)的元素会经由交换慢慢“浮”到数列的顶端。

  冒泡排序算法的运作如下:

    (1) 比较相邻的元素,如果前一个比后一个大,就把它们两个调换位置。

    (2) 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

    (3) 针对所有的元素重复以上的步骤,除了最后一个。

    (4) 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

 冒泡排序的代码如下:

/********** 冒泡排序:BuubbleSort.cpp **********/// 分类 -------------- 内部比较排序// 数据结构 ---------- 数组// 最差时间复杂度 ---- O(n^2)// 最优时间复杂度 ---- 如果能在内部循环第一次运行时,使用一个旗标来表示有无需要交换的可能,可以把最优时间复杂度降低到O(n)// 平均时间复杂度 ---- O(n^2)// 所需辅助空间 ------ O(1)// 稳定性 ------------ 稳定#include<iostream>#include<stdio.h>using namespace std;void swap(int a[], int i, int j){int temp=a[i];a[i]=a[j];a[j]=temp;} void BubbleSort(int a[],int len){int i,j;for(i=0;i<len-1;i++) //一共进行len-1次;每次最大元素就像气泡一样"浮"到数组的最后{for(j=0;j<len-i-1;j++)   {if(a[j]>a[j+1])  //大数后移;如果条件改成A[i] >= A[i + 1],则变为不稳定的排序算法    swap(a,j,j+1);}}}int main(){int a[8]={6,5,3,1,8,7,2,4};  BubbleSort(a,8);cout<<"排序结果:";for(int i=0;i<8;i++)    cout<<a[i]<<" ";cout<<endl;return 0;} 

  上述代码对序列{ 6, 5, 3, 1, 8, 7, 2, 4 }进行冒泡排序的实现过程如下

    

  

  2. 选择排序(Selection Sort)

 

  选择排序也是一种简单直观的排序算法。它的工作原理很容易理解:

    (1) 初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;

    (2) 然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。

    (3) 以此类推,直到所有元素均排序完毕。

  注意选择排序与冒泡排序的区别:冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放到合适的位置;而选择排序每遍历一次都记住了当前最小(大)元素的位置,最后仅需一次交换操作即可将其放到合适的位置。

  选择排序的代码如下:

/********** 选择排序:SelectionSort.cpp **********/// 分类 -------------- 内部比较排序// 数据结构 ---------- 数组// 最差时间复杂度 ---- O(n^2)// 最优时间复杂度 ---- O(n^2)// 平均时间复杂度 ---- O(n^2)// 所需辅助空间 ------ O(1)// 稳定性 ------------ 不稳定#include<iostream>using namespace std;void Swap(int a[], int i, int j){int temp=a[i];a[i]=a[j];a[j]=temp;}void SelectionSort(int a[], int len){int i,j;for(i=0;i<len-1;i++){int min=i;for(j=i+1;j<len;j++){if(a[j]<a[min]){min=j;}}if(min!=i){Swap(a,min,i);}}}int main(){int a[8]={6,5,3,1,8,7,2,4};int i;SelectionSort(a,8);cout<<"排序结果:";for(i=0;i<8;i++)   cout<<a[i]<<" ";return 0;} 


   上述代码对序列{ 8, 5, 2, 6, 9, 3, 1, 4, 0, 7 }进行选择排序的实现过程如右图  

  选择排序是不稳定的排序算法,不稳定发生在最小元素与A[i]交换的时刻。

  比如序列:{ 5, 8, 52, 9 },一次选择的最小元素是2,然后把2和第一个5进行交换,从而改变了两个元素5的相对次序。

 

  3. 插入排序(Insertion Sort)

  插入排序是一种简单直观的排序算法。它的工作原理非常类似于我们抓扑克牌

      

 

  对于未排序数据(右手抓到的牌),在已排序序列(左手已经排好序的手牌)中从后向前扫描,找到相应位置并插入。

  插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

  具体算法描述如下:

    (1) 从第一个元素开始,该元素可以认为已经被排序

    (2) 取出下一个元素,在已经排序的元素序列中从后向前扫描

    (3) 如果该元素(已排序)大于新元素,将该元素移到下一位置

    (4) 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置

    (5) 将新元素插入到该位置后

    (6) 重复步骤2~5

  插入排序的代码如下:

/********** 插入排序:InsertSort.cpp **********/// 分类 ------------- 内部比较排序// 数据结构 ---------- 数组// 最差时间复杂度 ---- 最坏情况为输入序列是降序排列的,此时时间复杂度O(n^2)// 最优时间复杂度 ---- 最好情况为输入序列是升序排列的,此时时间复杂度O(n)// 平均时间复杂度 ---- O(n^2)// 所需辅助空间 ------ O(1)// 稳定性 ------------ 稳定#include<iostream>using namespace std;void InsertSort(int a[], int len){int i,j,get;for(i=1;i<len;i++){get=a[i];  //右手抓到一张新扑克牌j=i-1;     //左手有序的手牌while(j>=0 && get<a[j])  //将抓到的新牌与手牌从右往左比较 {a[j+1]=a[j];  //如果新牌比手牌大,手牌后移 j--;          //继续往左比较 } a[j+1]=get;    //将新抓到的牌放到合适的位置,形成新的有序手牌 }}int main(){int a[8]={6,5,3,1,8,7,2,4};InsertSort(a,8);cout<<"排序结果:";for(int i=0;i<8;i++)    cout<<a[i]<<" ";cout<<endl;return 0;}  

   上述代码对序列{ 6, 5, 3, 1, 8, 7, 2, 4 }进行插入排序的实现过程如下

      

  插入排序不适合对于数据量比较大的排序应用。但是,如果需要排序的数据量很小,比如量级小于千,那么插入排序还是一个不错的选择。 插入排序在工业级库中也有着广泛的应用,在STL的sort算法和stdlib的qsort算法中,都将插入排序作为快速排序的补充,用于少量元素的排序(通常为8个或以下)。

 

  3.1 插入排序的改进:二分插入排序

 

  对于插入排序,如果比较操作的代价比交换操作大的话,可以采用二分查找法来减少比较操作的次数,我们称为二分插入排序,代码如下:

/********** 二分插入排序:InsertSortDichotomy.cpp **********/// 分类 -------------- 内部比较排序// 数据结构 ---------- 数组// 最差时间复杂度 ---- O(n^2)// 最优时间复杂度 ---- O(nlogn)// 平均时间复杂度 ---- O(n^2)// 所需辅助空间 ------ O(1)// 稳定性 ------------ 稳定#include<iostream>using namespace std;void InsertSortDichotomy(int a[], int len){int i,j,get;int left,mid,right;for(i=1;i<len;i++){get=a[i];  //新抓的扑克牌left=0;right=i-1;while(left<=right)  //采用二分法定位新牌位置 {mid=(left+right)/2;if(a[mid]>get)right=mid-1;elseleft=mid+1;}for(j=i-1;j>=left;j--)  //将欲插入的新牌位置右边的牌整体右移一个单位 a[j+1]=a[j];a[left]=get;   //将抓到的新牌插入到手牌中 }}int main(){int a[8]={6,5,3,1,8,7,2,4};  InsertSortDichotomy(a,8);cout<<"排序结果:";for(int i=0;i<8;i++)    cout<<a[i]<<" ";cout<<endl;return 0;} 

  当n较大时,二分插入排序的比较次数比直接插入排序的最差情况好得多,但比直接插入排序的最好情况要差,所当以元素初始序列已经接近升序时,直接插入排序比二分插入排序比较次数少。二分插入排序元素移动次数与直接插入排序相同,依赖于元素初始序列。

 

  3.2 插入排序的更高效改进:希尔排序(Shell Sort)

 

  希尔排序,也叫递减增量排序,是插入排序的一种更高效的改进版本。希尔排序是不稳定的排序算法。

  希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

  我们来看下希尔排序的基本步骤,在此我们选择增量gap=length/2,缩小增量继续以gap = gap/2的方式,这种增量选择我们可以用一个序列来表示,{n/2,(n/2)/2...1},称为增量序列。希尔排序的增量序列的选择与证明是个数学难题,我们选择的这个增量序列是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。此处我们做示例使用希尔增量。


  希尔排序的代码如下:

/********** 希尔排序:ShellSort.cpp **********/// 分类 -------------- 内部比较排序// 数据结构 ---------- 数组// 最差时间复杂度 ---- 根据步长序列的不同而不同。已知最好的为O(n(logn)^2)// 最优时间复杂度 ---- O(n)// 平均时间复杂度 ---- 根据步长序列的不同而不同。// 所需辅助空间 ------ O(1)// 稳定性 ------------ 不稳定//代码参考:http://blog.csdn.net/morewindows/article/details/6668714  #include<iostream>using namespace std;void ShellSort1(int a[], int n)  //按照定义的希尔排序 {      int i, j, gap;        for(gap=n/2; gap>0; gap/=2) //初始步长为n/2,每次减半,直至最后为1      {        for(i=0; i<gap; i++)        //直接插入排序          {              for(j=i+gap;j<n;j+=gap)               {                if(a[j]<a[j-gap])                  {                      int temp=a[j];                      int k=j-gap;                      while(k>=0 && a[k]>temp)                      {                          a[k+gap]=a[k];                          k=k-gap;                      }                      a[k+gap]=temp;                  }              }        }      }}  void ShellSort2(int a[], int n)  //更为精简的希尔排序:每次从数组第gap个元素开始,每个元素与自己组内的数据进行直接插入排序{      int j, gap;            for(gap=n/2;gap>0;gap/=2){         for(j=gap;j<n;j++)//从数组第gap个元素开始          {            if(a[j]<a[j-gap])//每个元素与自己组内的数据进行直接插入排序              {                  int temp=a[j];                  int k=j-gap;                  while(k>=0 && a[k]>temp)                  {                      a[k+gap]=a[k];                      k-=gap;                  }                  a[k+gap]=temp;              }} }  }  int main(){int a[8]={6,5,3,1,8,7,2,4};  ShellSort2(a,8);cout<<"排序结果:";for(int i=0;i<8;i++)    cout<<a[i]<<" ";cout<<endl;return 0;}


  4. 归并排序(Merge Sort)

 

  归并排序是创建在归并操作上的一种有效的排序算法,效率为O(nlogn),1945年由冯·诺伊曼首次提出。

    1. 递归实现: 是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。


合并相邻有序子序列: 再来看看阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。


    2. 非递归(迭代)实现: 首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

  归并排序算法主要依赖归并(Merge)操作。归并操作指的是将两个已经排序的序列合并成一个序列的操作,归并操作步骤如下:

    (1) 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

    (2) 设定两个指针,最初位置分别为两个已经排序序列的起始位置

    (3) 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

    (4) 重复步骤3直到某一指针到达序列尾

    (5) 将另一序列剩下的所有元素直接复制到合并序列尾

  归并排序的代码如下:

/********** 归并排序:MergeSort.cpp **********/// 分类 -------------- 内部比较排序// 数据结构 ---------- 数组// 最差时间复杂度 ---- O(nlogn)// 最优时间复杂度 ---- O(nlogn)// 平均时间复杂度 ---- O(nlogn)// 所需辅助空间 ------ O(n)// 稳定性 ------------ 稳定#include<iostream>#include<stdio.h>using namespace std;void Merge(int A[], int left, int mid, int right) //合并两个已排好序的数组A[left...mid]和A[mid+1...right]{int len = right-left+1; int *temp=new int[len]; //定义数组temp来存放合并后的序列,大小为len int index=0;int i=left;  //左指针 int j=mid+1; //右指针 while(i<=mid && j<=right){//temp[index++] = A[i] <= A[j] ? A[i++] : A[j++];  //精简高效版本代码:将A[i]和A[j]中较小的赋值给temp[index],相应的下标加1 if(A[i]<=A[j]){temp[index]=A[i];i++;index++;}else if(A[j]<A[i]){temp[index]=A[j];j++;index++;}}while(i<=mid) //左边的序列并未全部赋值到temp中,继续赋值 {// temp[index++] = A[i++];temp[index]=A[i];i++;index++;}while(j<=right) //右边的序列并未全部赋值到temp中,继续赋值 {// temp[index++] = A[j++];temp[index]=A[j];j++;index++;}for(int k=0;k<len;k++)  //将temp[0-len]赋值到A数组中对应位置 {// A[left++] = temp[k];A[left]=temp[k];left++;}}void MergeSortRecursion(int A[], int left, int right) //递归实现的归并排序(自顶向下){if(left==right) //当待排序的序列长度为1时,递归开始回溯,进行merge操作    return;int mid=(left+right)/2;MergeSortRecursion(A,left,mid);MergeSortRecursion(A,mid+1,right);Merge(A,left,mid,right);}void MergeSortIteration(int A[], int len) //非递归(迭代)实现的归并排序(自底向上){int left, mid, right; //子数组索引,前一个为A[left...mid],后一个子数组为A[mid+1...right]for(int i=1;i<len;i=i*2) //子数组的大小i初始为1,每轮翻倍{left=0;while(left+i<len) //后一个子数组存在(需要归并){mid=left+i-1;right = mid+i < len ? mid+i : len-1; //后一个子数组大小可能不够Merge(A,left,mid,right);left=right+1; //前一个子数组索引向后移动}}}int main(){    int A1[] = { 6, 5, 3, 1, 8, 7, 2, 4 };      // 从小到大归并排序    int A2[] = { 6, 5, 3, 1, 8, 7, 2, 4 };    int n1 = sizeof(A1) / sizeof(int);    int n2 = sizeof(A2) / sizeof(int);    MergeSortRecursion(A1, 0, n1 - 1);          // 递归实现    MergeSortIteration(A2, n2);                 // 非递归实现    printf("递归实现的归并排序结果:");    for (int i = 0; i < n1; i++)    {        printf("%d ", A1[i]);    }    printf("\n");    printf("非递归实现的归并排序结果:");    for (int i = 0; i < n2; i++)    {        printf("%d ", A2[i]);    }    printf("\n");    return 0;}


  上述代码对序列{ 6, 5, 3, 1, 8, 7, 2, 4 }进行归并排序的实例如下 

 

       

  归并排序除了可以对数组进行排序,还可以高效的求出数组小和(即单调和)以及数组中的逆序对,详见这篇博文。

 

 

  5. 堆排序(Heap Sort)

  

  堆排序是指利用堆这种数据结构所设计的一种选择排序算法。堆是一种近似完全二叉树的结构(通常堆是通过一维数组来实现的),并满足性质:以最大堆(也叫大根堆、大顶堆)为例,其中父结点的值总是大于它的孩子节点。

  堆排序:在输出堆顶的最小值之后,使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素的次小值。如此反复执行,便能得到一个有序序列

  堆排序(Heap Sort)只需要一个记录元素大小的辅助空间(供交换用),每个待排序的记录仅占有一个存储空间。

 

5.1 堆的存储

  一般用数组来表示堆,若根结点存在序号0处, i结点的父结点下标就为(i-1)/2。i结点的左右子结点下标分别为2*i+12*i+2

  (注:如果根结点是从1开始,则左右孩子结点分别是2i和2i+1。)

  如第0个结点左右子结点下标分别为1和2。

  如最大化堆如下:

          

 

  左图为其存储结构,右图为其逻辑结构。

5.2 堆排序的实现

  实现堆排序需要解决两个问题:

    1.如何由一个无序序列建成一个堆?

    2.如何在输出堆顶元素之后,调整剩余元素成为一个新的堆?

  先考虑第二个问题,一般在输出堆顶元素之后,视为将这个元素排除,然后用表中最后一个元素填补它的位置,自上向下进行调整:首先将堆顶元素和它的左右子树的根结点进行比较,把最小的元素交换到堆顶;然后顺着被破坏的路径一路调整下去,直至叶子结点,就得到新的堆。我们称这个自堆顶至叶子的调整过程“筛选”。

  从无序序列建立堆的过程就是一个反复“筛选”的过程。

(1) 构造初始堆

  初始化堆的时候是对所有的非叶子结点进行筛选。最后一个非终端元素的下标是[n/2]向下取整,所以筛选只需要从第[n/2]向下取整个元素开始,从后往前进行调整。

  比如,给定一个数组,首先根据该数组元素构造一个完全二叉树。

  然后从最后一个非叶子结点开始,每次都是从父结点、左孩子、右孩子中进行比较交换,交换可能会引起孩子结点不满足堆的性质,所以每次交换之后需要重新对被交换的孩子结点进行调整。

(2) 进行堆排序

  有了初始堆之后就可以进行排序了。堆排序是一种选择排序。建立的初始堆为初始的无序区。

  排序开始,首先输出堆顶元素(因为它是最值),将堆顶元素和最后一个元素交换,这样,第n个位置(即最后一个位置)作为有序区,前n-1个位置仍是无序区,对无序区进行调整,得到堆之后,再交换堆顶和最后一个元素,这样有序区长度变为2。

  不断进行此操作,将剩下的元素重新调整为堆,然后输出堆顶元素到有序区。每次交换都导致无序区-1,有序区+1。不断重复此过程直到有序区长度增长为n-1,排序完成。

(3) 堆排序实例

   首先,建立初始的堆结构如图:

  

  然后,交换堆顶的元素和最后一个元素,此时最后一个位置作为有序区(有序区显示为黄色),然后进行其他无序区的堆调整,重新得到大顶堆后,交换堆顶和倒数第二个元素的位置……

  重复此过程:

  

最后,有序区扩展完成即排序完成:

                            

  由排序过程可见,若想得到升序,则建立大顶堆,若想得到降序,则建立小顶堆

(4) 代码

  假设排列的元素为整型,且元素的关键字为其本身。

  因为要进行升序排列,所以用大顶堆。

  根结点从0开始,所以i结点的左右孩子结点的下标为2i+1和2i+2。

#include<iostream>  using namespace std;  void HeapAdjust(int A[],int parent,int length) //对无序堆进行调整,使得A[]成为一个大顶堆 {int temp=A[parent];   //temp保存当前父节点int child=2*parent+1; //假设根节点的序号为0,则i节点的左孩子为2i+1,右孩子为2i+2while(child<length){//若右孩子存在,并且右孩子结点的值大于左孩子结点,则选取右孩子结点if(child+1<length && A[child]<A[child+1])    child++;    //如果父结点的值已经大于左右孩子结点的值,则直接结束if(temp>=A[child])    break;    //把孩子结点的值赋给父结点A[parent]=A[child];//选取孩子结点的左孩子结点,继续向下筛选parent=child;child=2*child+1;    }A[parent]=temp;}void HeapSort(int A[],int n) //堆排序 {//首先建立大顶堆for(int i=n/2;i>=0;i--) //非叶节点最大序号值为size/2    HeapAdjust(A,i,n-1);    //进行堆排序 for(int i=n-1;i>0;i--){//最后一个元素和第一个元素交换int temp=A[i];A[i]=A[0];A[0]=temp;//将剩下的无序元素继续调增为大顶堆HeapAdjust(A,0,i); } }  int main()  {      int A[8]={6,5,3,1,8,7,2,4};              HeapSort(A,8);      cout<<"排序结果:";            for(int i=0;i<8;i++)          cout<<A[i]<<" ";      cout<<endl;           return 0;  }   

  6. 快速排序(Quick Sort)

 

  快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序n个元素要O(nlogn)次比较。在最坏状况下则需要O(n^2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他O(nlogn)算法更快,因为它的内部循环可以在大部分的架构上很有效率地被实现出来。

  快速排序使用分治策略(Divide and Conquer)来把一个序列分为两个子序列。步骤为:

    (1) 快速排序首先找出一个元素作为基准(pivot)

    (2)然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值都不小于基准值。如此作为基准的元素调整到排序后的正确位置。

    (3)递归快速排序,将其他n-1个元素也调整到排序后的正确位置。

    (4) 对每个分区递归地进行步骤1~3,递归的结束条件是序列的大小是0或1,最后每个元素都是在排序后的正确位置,排序完成。

    举例说明:假设要排序的序列为

   (2)     2(i) 4 9 3 6 7 1 5(j)     首先用2当作基准,使用i和j两个指针分别从两边进行扫描。首先比较2和5,5比2大,j左移

   (2)     2(i) 4 9 3 6 7 1(j) 5     比较2和1,1小于2,所以把1放在2的位置

    (2)     1 4(i) 9 3 6 7 1(j) 5     比较2和4,4大于2,因此将4移动到后面

    (2)     1 4(i) 9 3 6 7(j) 4 5     比较2和7,2和6,2和3,2和9,全部大于2,满足条件,因此不变

    经过第一轮的快速排序,元素变为 [1] 2 [4 9 3 6 7 5]。之后,在把2左边的元素进行快排,由于只有一个元素,因此快排结束。右边进行快排,递归进行,最终生成最后的结果。  

快速排序的代码如下:

/********** 快速排序:quickSort.cpp **********/// 分类 ------------ 内部比较排序// 数据结构 --------- 数组// 最差时间复杂度 ---- 每次选取的基准都是最大(或最小)的元素,导致每次只划分出了一个分区,需要进行n-1次划分才能结束递归,时间复杂度为O(n^2)// 最优时间复杂度 ---- 每次选取的基准都是中位数,这样每次都均匀的划分出两个分区,只需要logn次划分就能结束递归,时间复杂度为O(nlogn)// 平均时间复杂度 ---- O(nlogn)// 所需辅助空间 ------ 主要是递归造成的栈空间的使用(用来保存left和right等局部变量),取决于递归树的深度,一般为O(logn),最差为O(n)       // 稳定性 ---------- 不稳定#include<iostream>using namespace std;int QuickSort(int A[],int left,int right){if(left<right){int key=A[left];int low=left;int high=right;while(low<high){while(low<high && A[high]>key){high--;}A[low]=A[high];while(low<high && A[low]<key){low++;}A[high]=A[low];}A[low]=key;QuickSort(A,left,low-1);QuickSort(A,low+1,right); }}int main(){int A[8]={6,5,3,1,8,7,2,4};  QuickSort(A,0,7);cout<<"排序结果:";for(int i=0;i<8;i++)    cout<<A[i]<<" ";cout<<endl;return 0;}

  快速排序是不稳定的排序算法,不稳定发生在基准元素与A[tail+1]交换的时刻。

  比如序列:{ 1, 3, 4, 2, 8, 9, 8, 7, 5 },基准元素是5,一次划分操作后5要和第一个8进行交换,从而改变了两个元素8的相对次序。

原创粉丝点击