8种常用内部排序

来源:互联网 发布:网络诈骗300元立案吗 编辑:程序博客网 时间:2024/05/18 15:27

此文章转载

在公司实习了,由于公司的性质,以后的工作中用到算法&数据结构的知识可能会很少,很想把以前学的数据结构&算法的知识系统的回忆一下,但由于时间的原因,再加上我一直都很喜欢排序算法的思想。近期主要就排序这个问题做一个系统的介绍:冒泡排序,简单选择排序,直接插入排序,希尔排序,快速排序,归并排序,堆排序,基数排序。

排序的稳定性:假设 ,在排序前的序列中第i记录领先于第j个记录。如果排序后第i记录仍领先于第j个记录,则称所用的排序算法是稳定的;反之,所用的排序的算法是不稳定的。

对于内排序来说,算法的性能主要受3个方面影响:时间性能(衡量排序算法好坏的最重要的标志);辅助空间(程序在执行过程中需要的额外辅助空间);算法的复杂度(算法本省的复杂度,比如堆排序相对就比较复杂)。

 

交换两个数据主要有三种方法,下面算法实现:

[cpp] view plain copy
  1. int swap(int array[],int m,int n)//实现两个元素的交换的三种方法  
  2. {  
  3.     //第一种方法需要一个额外空间存储  
  4.     int temp;  
  5.     temp=array[m];  
  6.     array[m]=array[n];  
  7.     array[n]=temp;  
  8.     //第二种方法容易发生溢出现象  
  9.     /*array[m]=array[m]+array[n]; 
  10.     array[n]=array[m]-array[n]; 
  11.     array[m]=array[m]-array[n];*/  
  12.     //第三种方法采用位异或的信息存储方法  
  13.     /*array[m]=array[m]^array[n]; 
  14.     array[n]=array[m]^array[n]; 
  15.     array[m]=array[m]^array[n];*/  
  16.     return 0;  
  17. }  

1、冒泡排序

1、思想描述:一种交换排序,两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。

2、算法复杂度及稳定性:

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

空间复杂度:O(1)

稳定性:稳定性算法

3、算法实现:

/**************************************************************/

/********************冒泡排序算法******************************/

void BubbleSort(int array[],int length)

{

       int i,j;

       for(i=0;i<length-1;i++)

       {

              for(j=length-2;j>=i;j--)//从后朝前比较,将较小的放置前面

              {

                     if(array[j]>array[j+1])

                            swap(array,j,j+1);

              }

       }

}

 

上述算法能否进一步改进呢,我们发现对于已经有序的序列来说,外部仍然需要进行length-1次,显然这个时候不需要再继续后面的循环。为了实现这种想法,可以增加一个标志位来标记数组是否已经有序。

改进算法:

void BubbleSortImprove(int array[],int length)

{

       int i,j;

       boolflag=true;

       for(i=0;i<length-1&&flag;i++)

       {

              flag=false;//初始化为false

              for(j=length-2;j>=i;j--)//从后朝前比较,将较小的放置前面

              {

                     if(array[j]>array[j+1])

                     {

                            swap(array,j,j+1);

                            flag=true;  //此时发生数据交换,序列不是有序序列,标记为true

                     }

              }

       }

}

2、简单选择排序

1、算法思想描述:通过 次的关键字比较,从n-i+1个记录中选出关键字最小的记录,并和第 个记录交换。

2、算法复杂度及稳定性:

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

空间复杂度:O(1)

稳定性:稳定性算法

3、算法的具体实现:

/**************************************************************/

/********************简单选择排序******************************/

void SelectSort(int array[],int length)

{

       inti,j,min;

       for(i=0;i<length-1;i++)

       {

              min=i;               //记录length-i最小元素

              for(j=i+1;j<length;j++)

              {

                     if(array[j]<array[min])

                            min=j;

              }

              if(i!=min)

                     swap(array,i,min);

       }

}

3、直接插入排序

1、算法思想:假设前 个记录已经有序,将第 个记录插入到已经已经排好序的有序表中,从而得到一个有 个记录的新的有序表。

2、算法复杂度及稳定性

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

空间复杂度:O(1)

稳定性:稳定性算法

3、算法实现:

/**************************************************************/

/********************直接插入排序******************************/

void InsertSort(int array[],int length)

{

       inti,j,temp;

       for(i=1;i<length;i++)

       {

              if(array[i]<array[i-1])//需要将array[i]插入到有序数组中

              {

                     temp=array[i];  

                     for(j=i;j>0&&temp<array[j-1];j--)

                     {

                            array[j]=array[j-1];//记录后移

                     }

                     array[j]=temp;//将array[i]插入到正确位置

              }

       }            

}

4、希尔排序

1、算法思想:实质上就是直接插入排序的改进,先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。

2、算法复杂度及稳定性

平均时间复杂度:O(nlogn)-O(n^2)

空间复杂度:O(1)

稳定性:不稳定性算法

3、算法实现

/**************************************************************/

/********************希尔排序******************************/

void ShellSort(int array[],int length)

{

       inti,j,temp,gap;

       for(gap=length/2;gap>0;gap=gap/2)//增量序列

       {

              for(i=gap;i<length;i++)

              {

                     if(array[i]<array[i-gap])//将array[i]插入到增量子序列中

                     {

                            temp=array[i];

                            for(j=i;j>0&&temp<array[j-gap];j=j-gap)

                            {

                                   array[j]=array[j-gap];

                            }

                            array[j]=temp;

                     }

              }

       }

}

5、堆排序

1、算法思想:将待排序的序列构造成一个大顶堆。此时整个序列的最大值就是堆顶的根节点。将其移走(就是讲其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的 个序列重新构造堆,这样就会得到n个元素中的次大元素,如此反复,最终就会得到一个有序序列了。

2、算法复杂性及稳定性

平均时间复杂度:O(nlogn)

空间复杂度:O(1)

稳定性:不稳定性算法

3、算法的实现:

/**************************************************************/

/********************堆排序******************************/

 

void HeapAdjust(int array[],int local,int length)

{

       inti,temp;

       temp=array[local];

       for(i=2*local+1;i<length;i=2*local+1)//从该节点的两个孩子节点开始

       {

              if((i<length-1)&&array[i]<array[i+1])//比较local的两个孩子的大小,保留较大的下标

                     i++;

              if(temp>=array[i])//应插入位置

                     break;

              array[local]=array[i];

              local=i;

       }

       array[local]=temp;//插入

}

 

void HeapSort(int array[],int length)//堆排序

{

       int i;

       for(i=length/2-1;i>=0;i--)//从第一个非叶子节点开始自上而下的调整每一个子树

       {

              HeapAdjust(array,i,length);

       }

       for(i=length-1;i>=0;i--)

       {

              swap(array,0,i);    //将堆顶记录和当前未排序的子序列的最后一个记录交换

              HeapAdjust(array,0,i);//将剩下的记录重新调整为大顶堆

       }     

}

 

6、归并排序

1、算法思想:归并排序就是利用归并的思想,假设初始含有n个记录,把其看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到 个长度为1或2的有序子序列;然后在两两合并……,如此重复,直至得到一个长度为n的有序序列为止,成为2-路归并排序。

2、算法复杂度及稳定性:

平均时间复杂度:O(nlogn)

空间复杂度:O(n)

稳定性:稳定性算法

3、算法实现:

/**************************************************************/

/********************归并排序的递归形式************************/

 

void Merge(int array[],int tm[],int start,int mid,intend)//将有序的array[start...mid]和array[mid+1...end]合并为有序的tm[start...end]

{

       int i,j,k;

       k=start;

       i=start;

       j=mid+1;

       while(i<=mid&&j<=end)//将array中的记录有小到大归并入tm中

       {

              if(array[i]<=array[j])

                     tm[k++]=array[i++];

              else

                     tm[k++]=array[j++];

       }

       if(i<=mid)//将剩余的array[i...mid]复制到tm中

       {

              while(i<=mid)

                     tm[k++]=array[i++];

       }

       if(j<=end)//将剩余的array[j...end]复制到tm中

       {

              while(j<=end)

                     tm[k++]=array[j++];

       }

}

 

void MSort(int array[],int tm[],int start,int end)

{

       inttm2[MAXSIZE+1];

       if(start==end)//如果就一个记录可以直接归并

       {

              tm[start]=array[end];

       }

       else

       {

              intmid=(start+end)/2;

              MSort(array,tm2,start,mid);//首先将array[start...mid]归并为一个有序数组,并放在tm2[start..mid]中

              MSort(array,tm2,mid+1,end);//然后将array[mid+1...end]归并为一个有序数组,并放在tm2[mid+1...end]中

              Merge(tm2,tm,start,mid,end);//最后将tm2[start...mid]和tm2[mid+1...end]合并成一个有序记录,并放在tm[start...end]中

       }

}

 

 

void MergeSort(int array[],int length)

{

       MSort(array,array,0,length-1);//归并排序的递归形式

}

 

 

 

7、快速排序

1、算法思想:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字记录均比另一部分的关键字小,然后分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

2、算法复杂度及稳定性分析:

平均时间复杂度:O(nlogn)

空间复杂度:O(logn)-O(n)

稳定性:不稳定性算法:

3、算法实现:

/**************************************************************/

/***********************快速排序******************************/

 

int Partition(int array[],int low, int high)//交换顺序表array中字表的记录,使枢轴到位,并返回其所在的位置

{

       intmid=low+(high-low)/2;//计算数组中间的元素的下标

       if(array[low]>array[high])//交换左端和右端数据,保证左端数据较小

              swap(array,low,high);

       if(array[high]<array[mid])//交换右端和中间数据,保证中间数据较小

              swap(array,mid,high);

       if(array[low]>array[mid])//交换左端和中间数据,保证左端数据较小

              swap(array,low,mid);

 

       intpivot=array[low];//用第一个记录作为枢轴的记录

       while(low<high)

       {

              while(low<high&&array[high]>=pivot)

                     high--;

              array[low]=array[high];//直接替换,不需要交换,将比枢轴小的记录放到低端

              while(low<high&&array[low]<=pivot)

                     low++;

              array[high]=array[low];//直接替换,不需要交换,将比枢轴大的记录放到高端

       }

       array[low]=pivot;

       returnlow;//返回枢轴的位置

}

 

void QSort(int array[],int low,int high)

{

       if(low<high)

       {

              intpartition=Partition(array,low,high);//将array[low...high]一分为二,算出枢轴的位置

              QSort(array,low,partition-1);

              QSort(array,partition+1,high);

       }

}

 

void QuictSort(int array[],int length)

{

       QSort(array,0,length-1);  

}

改进主要有优化枢轴记录的选取,一种取中间,随机选取;用替换替代交换;对于记录较少的可以直接使用直接插入排序;尾递归的方法缩减堆栈的深度。

void QSort1(int array[],int low,int high)//尾递归

{

       if(low<high)

       {

              intpartition=Partition(array,low,high);//将array[low...high]一分为二,算出枢轴的位置

              QSort1(array,low,partition-1);

              low=partition+1;//尾递归

       }

}

8、基数排序

1、算法思想:n个记录的关键字进行排序,每个关键字看成一个d元组: ,分为两种方式:LSD(由数值的最右边(低位)开始)和MSD(由数值的最左边(高位)开始)。只讲述LSD,排序时先按 的值,从小到大将记录分到r(称为基数)个盒子中,一次手机;然后在按 的值继续。

2、算法复杂度及稳定性分析:

平均时间复杂度:O(d(n+rd))

空间复杂度:O(rd)

稳定性:稳定性算法

基数排序可参考:http://blog.csdn.net/cjf_iceking/article/details/7943609

八种常见内部排序算法总结回顾

本文将常见的8种内部排序算法做一个总结,排序的重要性不言而喻,主要从算法思想,算法复杂度及稳定性(排序稳定对于某些特殊需求来说是不言而喻的),算法的具体实现(VC++6.0下C语言实现)。下面用一个表格就这八种算法做一个汇总:



简单排序:冒泡排序,简单选择排序,直接插入排序(1)从算法的性质上来看,八种算法分为两类:

改进算法排序:希尔排序,堆排序,归并排序,快速排序,基数排序。

(2)从平均时间性能而言,快速排序最佳,其所需时间最省,但快速排序在最坏的情况下的时间性能不如堆排序和归并排序。但对于后两者而言,在n较大时,归并所需时间较省,但所需辅助存储量最多。

(3)基数排序最适用于n值很大而关键字较小的序列。而对于基本有序的序列,冒泡和插入性能较佳。

(4)从方法的稳定性来比较,基数排序是稳定的,几种简单排序算法均是稳定性排序算法,而对于改进的算法,比如希尔排序,快速排序,堆排序均是不稳定排序。

综上所述:对于所有的排序算法,没有哪种算法是最优的,在实用是要根据不同的情况适当选择,甚至可以多种方法综合实用(比如快速排序可以和直接插入排序(对于基本有序的序列性能较佳))。