排序算法的总结

来源:互联网 发布:欧盟进出口数据 编辑:程序博客网 时间:2024/05/04 10:04
1、冒泡排序:
      冒泡排序的最简单的排序算法,它的主要思想是通过循环两两比较,然后让数值较大的那个元素出现到数列的末尾,就好像泡泡往上升一样。
       由于有两个循环体,所以它的时间复杂度为n的2次方,它有两个优点:1,编程复杂度低,容易写出代码;2,具有稳定性,即相同元素会保持原数列的相对顺序不变。
      代码如下:
<span style="font-size:12px;"> void bubble_sort(int a[],int n){  int i,j;  for(i =1;i<n;i++)  {     for(j=0;j<n-i;j++)                 {                                 if(a[j]>a[j+1])                                                 swap(a[j],a[j+1]);                 }  }}</span>
 
 
     改进方法是在比较阶段设置标志位,若然一轮下来都没有交换数据,即证明排序已经完成,可以退出
改进代码如下:
void BubbleSort(Elem R[], int n)        {           i = n;  ,        while (i >1)           {               lastExchangeIndex = 1;              for (j = 1; j < i; j++)              if (compare(A[j+1],A[j]))               {                  Swap(A[j],A[j+1]);                  lastExchangeIndex = j;                }               i = lastExchangeIndex;            }       }   }

 
2、选择排序
        选择排序的思想是按顺序选取一个元素来遍历比较,每次都找出本轮遍历最小(最大)的元素,并放置在数列开头(末尾),从而实现排序。
       这样每次遍历只需记住最小元素(最大)的序列号,然后再作交换,排序成功后交换的次数在0~n-1的范围内。比较次数为n(n-1)/2之。由于交换次数少,所以相比冒泡排序快。
       代码如下:
<span style="font-size:12px;">void choose_sort(int a[]){  int temp;  int i,j;  for(i=0;i<10;i++)  {      temp = i;      for(j=i;j<10;j++)                  {                    if(a[temp]>a[j])                                                temp = j;                  }                  if(i != temp)                  swap(a[i],a[temp]);  }}</span>

3、插入排序
        插入排序的思想是在有序数列中,找到插入元素应处的位置,然后有序数列中相对挪动腾出位置,再插入目标元素,则该数列仍为有序数列。
        需要额外的空间,并且需要挪动数据,带来额外的开销。时间复杂度为O(n的2次方)
      代码实现如下:
<span style="font-size:12px;">void insert_sort(int a[]){  int i,j,k;  int b[10]={0};  b[0] = a[0];  for(i=1;i<10;i++)  {                  for(j=0;j<i;j++)                  {                     if(a[i]>b[j])                                                 continue;                                 else                                                 break;                  }                                   for(k=i;k>j;k--)                                 b[k] = b[k-1];                  b[j] = a[i];  }  for(i =0;i<10;i++)                  a[i]=b[i];}</span>
查找的方法可以用折半查找法优化,这个以后再慢慢加强。
 
4,快速排序
        快速排序的算法思想就是通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再运用分治的方法对分开的两部分进行上面重复的操作,直到整个数列递归排序完成。
    代码实现如下:
<span style="font-size:12px;">int partition(int a[],int low, int high){                 int partition = a[low];                 while(high > low)                {                   while(a[high]>partition && high >low)                          --high;                   if(high > low)                   {                       a[low] = a[high];                                   ++ low;                   }                   while(a[low]<partition && high >low)                                      ++low;                   if(high > low)                   {                       a[high] = a[low];                                   -- high;                   }                }                a[low] = partition;                 return low;}void quick_sort(int a[],int low, int high){                 if(low<high)                {                   int part = partition(a,low,high);                   quick_sort(a,low,part-1);                   quick_sort(a,part+1,high);                }}</span>
 最坏情况下的的时间复杂度为n的2次方,一般为nlogn。
 
5、归并排序
         归并排序的思路就是,使用分割的办法将这个序列分割成一个一个已经排好序的子序列。然后再利用归并的方法将一个个的子序列合并成排序好的序列。其中分割使用了分治的思想,把数列不断分成两部分,直到子部分为两个以下为止,然后就不断递归把各子部分组合成有序的数列。
      代码如下:
<span style="font-size:12px;">void merge(int a[],int mid, int low,int high){    int i = low;                 int j = mid+1;                 int k = 0;                 int *b = new int[high-low+1];                 while(i <= mid && j <= high)                {                   if(a[i] <= a[j])                                   b[k++] = a[i++];                   else                       b[k++] = a[j++];                             }                 while(i <= mid)                                b[k++] = a[i++];                 while(j <= high)                                b[k++] = a[j++];                                 for(k = 0, i = low;i<=high;k++,i++)                                a[i] = b[k];              delete [] b;}void merge_sort(int a[],int low, int high){                 if(low < high)                {                   int mid = (low + high)/2;                   merge_sort(a,low,mid);                   merge_sort(a,mid+1,high);                   merge(a,mid,low,high);                }}</span>
       归并排序算法是一种O(nlogn)的算法。它的最差,平均,最好时间都是O(nlogn)。但是它需要额外的存储空间,这在某些内存紧张的机器上会受到限制。
       归并算法是又分割和归并两部分组成的。对于分割部分,如果我们使用二分查找的话,时间是O(logn),在最后归并的时候,时间是O(n),所以总的时间是O(nlogn)。
 
6、堆排序
        堆排序的思想是建立大顶堆,然后把堆顶元素与堆尾元素互换位置。然后再把堆长减1,再重新建立大顶堆,依次直到堆元素个数为1。以下是代码实现,先建立一个大顶堆,再把元素中0~n-1重新调整为大顶堆,然后每次都把最大的元素放在堆尾,直到排序完毕。
        代码实现如下:
<span style="font-size:12px;">void heap_adjust(int a[],int s, int len){                 int temp = a[s];                 for(int j=2*s+1;j<len;j=2*j+1)                {                   if(j<len && a[j]<a[j+1])                                    ++j;                   if(temp > a[j])                                   break;                   a[s] = a[j];                   s = j;                }                a[s] = temp;}void heap_sort(int a[],int len){   int i;                 for(i=(len-1)/2;i>=0;i--)                                heap_adjust(a,i,len-1);                 for(i=len;i>1;i--)                {                    swap(a[i],a[0]);                                heap_adjust(a,0,i-2);                }}</span>
      堆排序的最坏时间复杂度为O(nlogn),堆排序的平均时间复杂度为O(nlogn),空间复杂度为O(1)。由于建初始堆所需的比较次数较多,所以堆排序不适于记录数较少的文件。它是不稳定的排序方法。


7、希尔排序  
       希尔排序就是一种缩小增量排序。也就是按照步长分组,再在每组中进行插入排序。也即是将整个代拍数列分割成若干个子数列分别进行直接插入排序,待整个数列基本有序之后,在对全体记录进行一次直接插入排序。
       代码实现如下:
<span style="font-size:12px;">void shell_sort(int a[],int len){                 int m = (len+1)/2;                 int i ,j;                 while(m > 0)                {                   for(i = 0;i <= len;i++)                   {                                   for(j = i;(j+m)<=len;j+=m)                                  {                                      if(a[j]>a[j+m])                                        swap(a[j],a[j+m]);                                  }                   }                   m = m/2;                }}</span>

     
原始的算法实现在最坏的情况下需要进行O(n2)的比较和交换。V. Pratt对算法进行了少量修改,可以使得性能提升至O(n log2 n)。这比最好的比较算法的O(n log n)要差一些。这个算法步长的选取直接影响效率,至今貌似还没有找到最优的步长选取策略。
 
8、基数排序(桶排序)
        基数排序主要是通过关键字间的比较和移动记录这两种操作,而实现基数排序不需要进行记录关键字间的比较。它是一种借助多关键字排序的思想对但逻辑关键字进行排序的方法。简单来说就是通过分配和手机过程实现排序。

两种多关键码排序方法:

  最高位优先法(MSD法)。先按k1排序,将序列分成若干子序列,每个子序列中的记录具有相同的k1值;再按k2排序,将每个子序列分成更小的子序列;然后,对后面的关键码继续同样的排序分成更小的子序列,直到按kd排序分组分成最小的子序列后,最后将各个子序列连接起来,便可得到一个有序的序列。前面介绍的扑克牌先按花色再按面值进行排序的方法就是MSD法
  最次位优先法(LSD法)。先按kd排序,将序列分成若干子序列,每个子序列中的记录具有相同的kd值;再按kd-1排序,将每个子序列分成更小的子序列;然后,对后面的关键码继续同样的排序分成更小的子序列,直到按k1排序分组分成最小的子序列后,最后将各个子序列连接起来,便可得到一个有序的序列。前面介绍的扑克牌先按面值再按花色进行排序的方法就是LSD法。

基于LSD方法的链式基数排序的基本思想
  “多关键字排序”的思想实现“单关键字排序”。对数字型或字符型的单关键字,可以看作由多个数位或多个字符构成的多关键字,此时可以采用“分配-收集”的方法进行排序,这一过程称作基数排序法,其中每个数字或字符可能的取值个数称为基数。比如,扑克牌的花色基数为4,面值基数为13。在整理扑克牌时,既可以先按花色整理,也可以先按面值整理。按花色整理时,先按红、黑、方、花的顺序分成4摞(分配),再按此顺序再叠放在一起(收集),然后按面值的顺序分成13摞(分配),再按此顺序叠放在一起(收集),如此进行二次分配和收集即可将扑克牌排列有序。
 
 
  

这个感觉是个有限制的算法,所以暂时没写代码实现仅仅是了解思想。
      
时间效率:设待排序列为n个记录,d个关键码,关键码的取值范围为radix,则进行链式基数排序的时间复杂度为O(d(n+radix)),其中,一趟分配时间复杂度为O(n),一趟收集时间复杂度为O(radix),共进行d趟分配和收集。 空间效率:需要2*radix个指向队列的辅助空间,以及用于静态链表的n个指针。