【算法导论】排序算法 三

排序算法三

基本的声明及公用函数库在【算法导论】排序算法 零中介绍。

一、选择法排序、冒泡排序、插入法排序

二、快速排序、分治法排序、堆排序

三、计数排序、基数排序、桶排序

gtest介绍及测试用例如下:测试框架之GTest

MIT《算法导论》下载:hereorhttp://download.csdn.net/detail/ceofit/4212385 

源码下载:here orhttp://download.csdn.net/detail/ceofit/4218488

第一节讲了几个最简单的排序算法,时间复杂度都是n*n

第二节讲了几个稍微复杂的排序算法,时间复杂度可优化到nlogn,有些空间复杂度比较大。

本节进一步对时间进行优化,讲解几个时间复杂度最有情况下可达n ,但空间复杂度比较大的排序算法,典型的空间换取时间,而且这几个算法的使用范围有一定的限制。包括计数排序、基数排序、桶排序。 

计数排序

计数排序大家应该都知道,时间复杂度n,空间复杂度与数值范围有关。

计数排序建立一个大数组,数组大小为可能出现的数值的最大值,然后将排序序列的数值为下标的位置置1,由于下标隐含有序,所以可得到有序序列。为兼容存在相同的数值,将大数组初始化为0,每次置位操作改为++即可。

例如

2 4 5 3 1

建立一个6个元素的数组A

遍历待排序序列,置位

A[2]=1,A[4]=1,A[5]=1,A[3]=1,A[1]=1;

然后将A从0-5遍历查找值为1的元素下标即可。

结果是

1 2 3 4 5

有相同数值的序列将置位改为++。

//计数排序,空间换取时间O(n)且不通用。要求取值为整数,且范围与内存有关,排序性能与范围有关。
void CountingSort(int *pArray,int cnt);

为了通用,先获取最大值,使用动态开辟内存。如果已知数值范围,可不获取最大值。

#include "SK_Common.h"
#include "SK_Sort.h"

void CountingSort(int *pArray,int cnt)
{
    if(cnt <= 1)
        return;
    int i = 0,j = 0;
    int index = 0;

    //分配数组
    int maxnum = get_max(pArray,cnt);
    int *nums = new int[maxnum+1];

    //初始化
    for(i=0; i<maxnum; i++)
    {
        nums[i] = 0;
    }
    //置位
    for(i=0; i<cnt; i++)
    {
        nums[pArray[i]]++;
    }
    index = 0;
    //排序合并
    for(i=0; i<maxnum; i++)
    {
        if(nums[i] == 0)
        {
            continue;
        }
        for(j=0; j<nums[i]; j++)
        {
            pArray[index++] = i;
        }
    }
    delete[] nums;
}

 基数排序

基数排序来源于原始的卡片计算机,比如10进制数,可从低位到高位依次排序,最终得到的是有序序列。每位排序时可使用计数排序或其他方法。其中排m位时要求除m位有序外,前m-1位的相对顺序不变。这种称为稳定的排序,例如冒泡法就是一种稳定的排序。

这个举个书上的例子:

7个三位数,先按照个位排序,再排十位,再排百位,最终得到有序序列。

//基数排序,从低位到高位依次排序,要求对一个位排序是稳定的,一层排序后除此层大小外相对位置不变。
//对一位的排序希望是高效的。时间复杂度、空间复杂度与单个位排序算法有关。
void RadixSort(int *pArray,int cnt);

本例只求说明,使用冒泡排序,所以效率比较低,可修改为其他稳定的排序方法。

#include "SK_Common.h"
#include "SK_Sort.h"

//按4位比较
static int compareby4bit(int val1,int val2,int pos)
{
    int siftcnt = pos*4;
    int dmask = 0xf;
    int cmp1,cmp2;
    cmp1 = (val1>>siftcnt) & dmask;
    cmp2 = (val2>>siftcnt) & dmask;
    return cmp1-cmp2;
}

//冒泡法是稳定的,只是效率有点低。
void _radix_sort(int *pArray,int cnt,int pos)
{
    int i=0,j=0;
    for(i=0;i<cnt-1;i++)
    {
        for(j=cnt-1;j>i;j--)
        {
            if(compareby4bit(pArray[j-1],pArray[j],pos) > 0)
            {
                exchange(&(pArray[j]),&(pArray[j-1]));
            }
        }
    }
}

//按4位为一层
void RadixSort(int *pArray,int cnt)
{
    int i = 0;
    int maxbit;
    //获取位数,按4位为单位排序
    maxbit=get_bitcnt(get_max(pArray,cnt));
    for(i=0; i<(maxbit+3)/4; i++)
    {
        _radix_sort(pArray,cnt,i);
    }
}

桶排序

桶排序也是采用分而治之的原理,不过与分治法排序的区别在于

1、桶排序只分1层,分治法一层一层分到单个数值。

2、桶排序桶间先保证有序,然后各桶内再排序,最后合并。分治法是各部分先自己排序,部分直接不保证有序,最后合并。

例如

2 4 5 3 1

比如2个桶,可与3比较,<=3的一个桶,>3的一个桶

于是:

2 3 1

4 5

分别排序后为:

1 2 3

4 5

合并为:

1 2 3 4 5

//桶排序,先分桶,桶间有序,再桶内排序,最后合并。
//空间复杂度比较大,时间复杂度最优可为n
void BucketSort(int *pArray,int cnt);

本例分了10个桶,按照大小存储。

#include "SK_Common.h"
#include "SK_Sort.h"

//将pArray分到10个桶中,每个桶大小为cnt,依次排在tmpArray中,cntArray存储各个桶的大小。
static void _devid(int *pArray,int cnt,int *tmpArray,int*cntarray)
{
    int i = 0;
    int index;
    int max = get_max(pArray,cnt);
    int min = get_min(pArray,cnt);
    int tunit = (max-min+1)/10;

    if(tunit == 0)
        tunit = 1;
    //初始化个数
    for(i=0;i<10;i++)
    {
        cntarray[i] = 0;
    }
    //分桶
    for(i=0;i<cnt;i++)
    {
        index=(pArray[i]-min)/tunit;
        if(index > 9)
            index = 9;
        tmpArray[index*cnt+cntarray[index]] = pArray[i];
        cntarray[index]++;
    }
}

//将10个桶合并为1个数组
static void _combin(int *pArray,int cnt,int *tmpArray,int *cntArray)
{
    int index=0;
    int i,j;
    for(i=0;i<10;i++)
    {
        for(j=0;j<cntArray[i];j++)
        {
            pArray[index++] = tmpArray[i*cnt+j];
            if(index >= cnt)
                break;
        }
        if(index >= cnt)
            break;
    }
}

//假设分10个桶
void BucketSort(int *pArray,int cnt)
{
    int i = 0;
    int *tmpArray = new int[cnt*10];
    int cntArray[10];
    //分桶
    _devid(pArray,cnt,tmpArray,cntArray);
    //桶内排序
    for(i=0; i<10; i++)
    {
        BubbleSort(tmpArray+i*cnt,cntArray[i]);
    }
    //合并
    _combin(pArray,cnt,tmpArray,cntArray);

    delete[] tmpArray;
}

你可能感兴趣的:(算法,优化,测试,delete,存储,Exchange)