通俗理解九大排序算法

转自本人知乎文章:https://zhuanlan.zhihu.com/p/30311662算法

如今,但凡规模大一点的互联网公司招聘软件相关的岗位,都会对数据结构和算法有必定要求。做为非科班出身的程序yuan,要想进好一点的公司,仍是老老实实地把基础打扎实吧。数组

说到排序,你们应该都不陌生,由于你生活中确定有过网购吧,你在淘宝搜索宝贝的时候,遇到的就是排序,好比有按价格高低排序、按综合排序、按信用高低排序。因此排序算法应该能够说是算法里面很重要的一个分支。数据结构

开始以前,先简单介绍一下排序算法的几个重要指标,这里,我尽可能用本身理解的傻瓜式方法解读:数据结构和算法

(1)稳定性:当序列中存在两个或两个以上的关键字相等的时候,若是排序前序列中r1领先于r2,那么排序后r1若是仍旧领先r2的话,则是稳定的。(相等的元素排序后相对位置不变)ui

(2)不稳定性:当序列中存在两个或两个以上的关键字相等的时候,若是排序前序列中r1领先于r2,那么排序后r1若是落后r2的话,则是不稳定的。(相等的元素排序后相对位置发生改变)spa

(3)时间复杂度:算法的时间开销是衡量其好坏的最重要的标志。高效率的算法应该具备更少的比较次数和记录移动次数。3d

(4)空间复杂度:即执行算法所须要的辅助存储的空间。code

1、直接插入排序(插入类)blog

流程描述:遍历序列中的关键字,每次取一个待排序的关键字,从待排序关键字的前一个关键字逐次向前扫描,若是扫描到的关键字大于待排序关键字,则把扫描到的关键字日后移一个位置。最后找到插入位置,将待排序关键字插入。排序

void InsertSort(int R[],int n)
{
int i,j
int temp;
for(i=1;i<n;++i)
 {
temp=R[i];   //将待排关键字暂时存放在temp中
j=i-1;      //待排关键字的前一个关键字序号
while(j>=0&&temp<R[j])
//从待排关键字的前一个关键字开始扫描,若是大于待排关键字,则日后移一个位置
  {
  R[j+1]=R[j];
  --j; 
  }
  R[j+1]=temp; //找到插入位置,将temp中暂存的待排关键字插入
 }
}

最坏状况:整个序列是逆序的时候,则内层循环的条件temp<R[j]始终成立,此时对于每一次外层循环,内层循环次数每次达到最大值(即内层循环位i次),外层循环i取值为1~i-1,因此总的执行次数为n(n-1)/2 。

最好状况:整个序列为正序的时候。内层循环条件始终不成立,因此内层循环始终不执行,始终执行语句R[j+1]=temp。因此时间复杂度为O(n)。

空间复杂度:算法所需的辅助存储空间不随待排序列的规模变化而变化,是个常量,因此为O(1)。

2、折半插入排序(插入类)

过程描述:过程同直接插入排序,只是不一样于直接插入排序时用顺序查找,这里用的是折半查找。因此折半插入排序在查找过程上比直接插入排序节约很多时间。可是关键字移动次数和直接插入排序同样。

最好状况时间复杂度:

最坏状况时间复杂度:

平均状况时间复杂度:

 

3、冒泡排序(交换类)

过程描述:经过一系列的交换动做实现排序。首先第一个关键字和第二个关键字比较,若是第一个关键字大,两者交换;而后第二个关键字和第三个关键字比较,若是第二个关键字大,两者交换,不然不交换。一直进行下去,知道最终最大的哪一个关键字被交换到了最后,一趟冒泡排序完成。

void BubbleSort(int R[],int n)
{
int i,j,flag;
int temp;
for(i=n-1;i>=1;--i)
{
  flag=0;    //flag用来标记本趟排序是否发生了交换
  for(j=1;j<=i;++j)
  {
    if(R[j-1]>R[j])
     {
     temp=R[j];
     R[j-1]=R[j];
     R[j]=temp;
     flag=1;   //flag=1表示本次排序发生了交换
     }  
   if(flag==0)//若是没有发生交换,说明序列有序,排序结束
   return;
}
}

最坏状况:序列逆序,此时内层循环if语句的条件始终成立,基本操做执行的次数为n-i。i取值为1~n-1,因此总的执行次数为(n-1+1)(n-1)/2=n(n-1)/2,因此时间复杂度为O(n^2)。

最好状况:序列正序。此时内层循环的条件比较语句始终不成立,不发生交换,内层循环执行n-1次,因此时间复杂度为O(n)。

平均状况:时间复杂度O(n^2)。

4、简单选择排序(选择类)

void SelectSort(int R[],int n)
{
int i,j,k;
int temp;
  for(i=0;i<n;++i)
  {
    k=i;
    for(j=i+1;j<n;++j) //从i后面的序列中挑选一个最小的关键字
    {
       if(R[k]>R[j])
       k=j;            //
       temp=R[i];
       R[i]=R[k];
       R[k]=temp;
    }
  }
}

5、希尔排序(插入类)

过程 描述:重点在增量的选取。若是增量为m,那么将下标为0、m、2m、3m的关键字分红一组,将下标为一、m+一、2m+一、3m+1等关键字分红另一组,分别对这些组进行插入排序。这就是一趟希尔排序。

 

6、快速排序(交换类)

过程描述:每一趟选择当前子序列中的一个关键字做为枢轴(通常选择第一个关键字做为枢轴),将子序列中比枢轴小的移到枢轴前面,比枢轴大的移到枢轴后面,本趟交换完成后获得新的更短的子序列,成为下一趟交换的初始序列。一趟排序以后能够肯定枢轴的最终位置。比枢轴小的所有在枢轴左边,比枢轴大的所有在枢轴右边。

void QuickSort(int R[],int high,int low)
{
int temp;
int i=low,j=high;
if(low<high)
{
temp=R[low];
while(i!=j)
 {
    while(j>i&&R[j]>=temp) --j; //从右往左扫描,找到一个小于枢轴temp的关键字
    if(i<j)
    {
      R[i]=R[j];  //将右边小于枢轴temp的关键字放在temp的左边
      ++i;       //左边序列号向右移一位
    }
    while(i<j&&R[i]<temp) ++i;//从左向右扫描,找到一个大于枢轴关键字temp的关键字
    if(i<j)
     {
       R[j]=R[i];//将左边大于枢轴temp的关键字放在temp的右边
       --j;      //右边序号向左移动一位
     }  
  }
 R[i]=temp;
 QuickSort(R,low,i-1);
 QuickSort(R,i+1,high);
 }
}

最好状况:时间复杂度为O(nlogx_{2}n) ,待排序列越接近无序,本算法效率越高。

最坏状况:时间复杂度为 O(n^{2}) ,待排序列越接近有序,本算法效率越低。

平均状况:时间复杂度 O(nlogx_{2}n) 。

空间复杂度:从头至尾只用了temp这一个辅助存储,因此为O(1)。

7、堆排序(选择类)

把堆当作彻底二叉树,大根堆---父亲大孩子小;小根堆---父亲小孩子大。

过程描述:整个排序的过程就是不断地将序列调整为堆。

以原始序列:49 38 65 97 76 13 27 49为例,调整为大根堆。

(1)调整97,97>49,不须要调整

(2)调整65,65>13,65>27,不须要调整

(3)调整38,38<97,38<76。须要调整,38和97交换,交换后38成为49的根节点,,继续将38和49交换。

(4)调整49,49<97,49<65,因此49和较大者97交换,交换后,49<76,仍然不知足大根堆,将49与76交换。

8、2路归并排序

void mergeSort(int A[],int low,int high)
{
  if(low<high)
  {
    int mid=(low+high)/2;
    mergeSort(A,low,mid);   //归并排序前半段
    mergeSort(A,mid+1,high);//归并排序后半段
    merge(A,low,mid,high);  //把数组中的low到mid  和 mid+1到high的两段有序序列归并成一段有序序列
  }
}

9、基数排序

"多关键字排序",(1)最高位优先(2)最低位优先。例如最高位优先:先按最高位排成若干子序列,再对每一个子序列按次高位进行排序。

以下图,低位优先的排序过程:每一个桶至关于一个队列,先进先出规则。

最后获得的结果:最高为有序,最高位相同的关键字次高位有序,次高位相同的关键字最低位有序,因此整个序列有序。

相关文章
相关标签/搜索