码迷,mamicode.com
首页 > 编程语言 > 详细

算法导论学习之线性时间排序+排序算法稳定性终结

时间:2015-03-13 18:46:59      阅读:170      评论:0      收藏:0      [点我收藏+]

标签:排序算法   线性时间排序   排序算法稳定性   

前面我们学习的几种排序算法都是基于比较的,对于任何输入数据他们都是适用的,其最坏的时间复杂度不会低于nlgn;
但对于一些比较特殊的输入数据,我们可以不采取比较的方法而是采用其它的方法对其进行排序,以达到线性的时间复杂度。下面就来介绍三种这样的算法:计数排序,基数排序,桶排序(因为这几种算法不常见,我只实现了计数排序,其它两种排序用伪代码表示)。

一.计数排序
算法思想:给定n个位于0–k之间的数(k是一个不太大的整数),我们可以统计出每个数前面有多少个小于它的数,然后就可以直接确定这个数在数组中的位置了;比如说x前面有17个数,那么x就应该放到18号位置上去,当然如果有多个相同的元素我们不能需要将它们都放到一个位置上去,需要做些改变。时间复杂度:O(n+k)
代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

#define maxn 10000

///计数排序不是通过比较进行的排序,因而效率比较高
///但是只能对小范围的正整数进行排序

void CountingSort(int *a,int n)
{ ///计数排序:对0--k之间的n个数进行排序
        int k=0;
        for(int i=1;i<=n;i++)
              k=max(k,a[i]);
        int c[maxn],b[maxn];   ///c用来记录中间结果,b用来记录排序结果
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)
             c[a[i]]++;      ///统计a[i]出现的次数
        /*以前我都采用下面这种方式进行计数排序
             int cnt=0;
             for(int i=0;i<=k;i++)
                   while(c[i]]
                   {
                         b[cnt++]=i;
                         c[i]--;
                    }
             这样也能得到结果,但这样其实是不稳定的排序方法。
        */
        for(int i=1;i<=k;i++)
             c[i]=c[i]+c[i-1];   ///统计不小于i的数的个数
        for(int i=n;i>=1;i--)   ///从后往前是为了使得排序稳定
        {
              b[c[a[i]]]=a[i];   ///在a[i]出现的位置记录a[i];
              c[a[i]]--;   ///如果有多个相同的元素x,我们就每次令c[x]的值减少1
        }
       for(int i=1;i<=n;i++)
            a[i]=b[i];     ///将排序结果复制回原数组
}

int main()
{
      int n=5,a[10];
      cout<<"请输入"<<n<<"个数:"<<endl;
      for(int i=1;i<=n;i++)
           cin>>a[i];
      CountingSort(a,n);
      cout<<"排序以后的数组:"<<endl;
      for(int i=1;i<=n;i++)
          cout<<a[i]<<" ";
      cout<<endl;
   return 0;
}

二.基数排序
算法思想:基数排序其实就是对n个d位的数进行排序,每个数的每一位都需要考虑。按照我们一般的想法,我们应该先按最高位进行排序,然后再按次高位进行排序。。。。但基数排序的思想与此相反,它先按照最低位进行一次排序,然后再按照第二位。。。。。最后是最高位,当然我们需要选择一种合适的算法来进行每次按位的排序。
伪代码如下:

RadixSort(A,d)
   for(int i=1;i<=d)
       do use a stable sort to sort array A on digit it

三.桶排序
桶排序处理的是输入元素均匀的落在[0,1)区间之间的情况。其思想就是将区间[0,1)划分成n个同样大小的区间,或称为桶,然后对每个桶内的元素进行排序,然后就可以直接输出了。因为输入是均匀的,所以这些元素在这些桶内分布应该也是比较均匀的,不会出现很多元素落在某个桶内的情况,所以有比较高的效率。
桶我们采用链表实现;对于元素a[i],我们将它放到编号为floor(na[i])的桶内,桶的内部的排序我们可以采用插入排序。
算法伪代码如下:

BucketSort(A)
{
    n = length[A];
    for(int i=1;i<=n;i++)
        do insert A[i] into list B[floor(nA[i])]
    for(int i=0;i<n;i++)
        do sort list B[i] with insertion sort
     concatenta the lists B[0],B[1],...,B[n-1]   together in order
}

四.各种排序算法稳定性终结
为什么要提出稳定性这个概念?因为在实际中我们一般是按记录中的某个关键值对一组记录进行排序,比如说某组记录含有身高,体重,年龄三个量,然后我们需要对其按年龄进行排序。这时我们希望如果关键值相等的时候,先输入的数据应该还是排在前面,而不是随便排。那么前面我们学习的排序算法那些是稳定的,那些是不稳定的呢?
我们先给出一个总的结论,然后再来依次简要说明一下:
稳定的排序算法有: 插入排序,合并排序,计数排序,基数排序。
不稳定的排序算法有:堆排序,快速排序。

1.插入排序
对于插入排序,相同的元素在前面插入的也总是排在前面,所以肯定是稳定的

2.合并排序
对于合并排序,我们重点分析它的合并过程;在合并左右过程只要我们保证在比较的时候如果两个元素大小相同,我们就把坐标小的放到前面就可以保证算法是稳定的了。

3.堆排序
无论是建堆还是堆排序的过程都是不断调整的过程,考虑节点i ,和i+1有相同的子节点,然后某次调整的时候节点i+1的子节点往上调整了而i的子节点没有调整;这种情况说明堆排序是不稳定的。

4.快速排序
快排的交换发生在划分的时候,有一种不稳定的情况就是把关键元素(key)交换到中间时会使得它和后面一段元素的相对顺序被打乱;所以快排也是不稳定的

5.计数排序
计数排序最后确定每个数的位置时,是从后往前确定的,所以可以保证相同的元素,原来在后面的排序以后还是在后面;所以计数排序是稳定的。

6.基数排序
如果基数排序在按每一位进行排序时选择的是一个稳定的排序方法,那么它也是稳定的。

算法导论学习之线性时间排序+排序算法稳定性终结

标签:排序算法   线性时间排序   排序算法稳定性   

原文地址:http://blog.csdn.net/acm_lkl/article/details/44244283

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!