排序算法小结-归并、基数排序

1.归并排序
归并排序用的是分治的思想,1个元素一定是有序的,2个元素分别对1个元素使用Merge()将其变成有序序列。4个元素可以分别对2个有序的元素使用Merge()将其变成有序的序列…
首先是重要的Merge()的实现,改函数完成的是[i……m]和[m+1……n]区间的归并(注意是闭区间,因此判断的时候都有等号的):

void Merge(int*r,int* rf,int i,int m,int n)
{
    if(r == NULL || rf == NULL || i > n) return;
    int pos = i;//记录本次归并的起点
    int k=i;
    int j = m+1;
    while(i <= m && j <= n)
    {
        if(r[i] < r[j]) rf[k++] = r[i++];
        else rf[k++] = r[j++];
    }
    while(i <= m) rf[k++] = r[i++];
    while(j <= n) rf[k++] = r[j++];
    //上面完成的操就就是把r数组的[i……m]和[m+1……n]区间的元素归并到rf中。
    for(;pos<=n;++pos)//这个赋值很重要,将归并好的元素重新赋值给r,
    //为下次归并做准备(最开始就是因为没有考虑到这一点,导致算法错误)
        r[pos] = rf[pos];
}

下面是归并排序算法的非递归版本:

void MergeSort(int*r,int*rf,int length)
{
    //非递归版本就是按照正常的思路来归并
    //1.把两个元素归并;
    //2.根据前一步把四个元素归并;
    //3....直至全部数据归并
    //注意要考虑不能序列尾部多出的元素。
    if(r==NULL || rf == NULL || length < 0 ) return;
    int len = 1;//元素归并的步长,一个元素是有序
    while( len  < length)
    {
        int s= len;
        len = s*2;//每次二倍的增长,这是算法时间复杂度中log2n的来源
        int i = 0;//每次归并处理从序列的起始位置开始
        while(i+len <= length)
        {
            Merge(r,rf,i,i+s-1,i+len-1);//将序列组归并
            i = i+len;//在序列上移动归并窗口
        }
        if(i+s <= length)//处理尾部多出来的元素,因为分组是在前次的基础上完成
        //的(2倍的关系)因此多出来的元素一定不小于i+s个。
            Merge(r,rf,i,i+s-1,length-1);
    }
    //由于在Merge中做了r和rf的交换操作,因此这里无需再有任何交换操作,
    //最后有序的数据会保存在r中。
}

下面是归并排序的递归实现:

void MergeSortRec(int* r,int* rf,int start,int length)
{
    //递归实现的思路是,现将序列2分,接着在2分,。。。直至划分好的序列只有一个元素
    //(一个元素是有序的),在调用Merge函数,归并。(有点类似于二叉树的后续便利)
    if(r==NULL || rf == NULL || length < 0 ) return;
    if(start < length)
    {
        int m = (start+length)/2;
        MergeSortRec(r,rf,start,m);//序列前半段二分
        MergeSortRec(r,rf,m+1,length);//序列后半段二分
        Merge(r,rf,start,m,length);//归并分段序列
    }
}

算法思路代码注释说的很清楚了,下面看看算法的特性:
1.算法是稳定的,因为在归并函数Merge中是按照元素的位置比较的,因此在相等元素的相对位置不会被改变。
2.时间复杂度为O(nlog2n),从非递归的实现版本可以简单推算时间算法的时间复杂度,内层循环不断的/2因此为log2n,完成循环当然是n啦。大概能够看出算法的时间富复杂度是O(nlog2n)。而且这种复杂度也是稳定的,不管序列是否有序都需要完成算法的所有操作。
3.空间复杂度,这个比较明细,在归并函数中需要使用与序列一样大的空间来保存归并结果,因此空间复杂度自然为O(n)了。

2.基数排序
基数排序是一种基于桶排序变种,感觉思路清晰了,实现起来不是很难,先看代码:

//链式基数排序的实现
//链式结构体
struct node
{
    int data;
    node* next;
};//存储带排元素结构体
node* f[10];
node* e[10];
int keyNum = 3;//最长关键字的长度

int getKeyPosNum(int num,int keyPos)
{
    //获得关键字每个位上的数字
    if(keyPos == 0) return -1;
    if(keyPos == 1) return num%10;
    if(keyPos == keyNum)
    {
        int n = 1;
        for(int i=1;i<keyNum;++i)
            n *= 10;
        return num/n;
    }   
    for(int i=1;i<keyPos;++i)
        num /= 10;
    return num%10;

}

void Distribute(node* l,int keyPos)
{
    //分配
    if(l == NULL || keyPos < 0 || keyPos > keyNum) return;
    node* p = l->next;
    while(p)
    {
        int key = getKeyPosNum(p->data,keyPos);
        node* q = p->next;
        e[key]->next->next = p;
        e[key]->next = p;
        p->next = NULL;
        p = q;
    }
}

void Collect(node* l)
{
    //收集
    if(l == NULL) return;
    node *p = l;
    for(int i=0;i<10;++i)
    {
        node* q = f[i]->next;
        while(q)
        {
            p->next = q;
            p = q;
            q = q->next;    
        }
        e[i]->next = f[i];
        f[i]->next = NULL;
    }
}
void RadixSort(node* l)
{
    if(l == NULL)
        return;
    for(int i=1;i<=keyNum;++i)
    {
        Distribute(l,i);
        Collect(l);
    }
}
int main()
{
    cout<<"输入排序元素:";
    int data;
    node* header =  new node();
    header->data = -1;
    header->next = NULL;
    node* p = header;
    while(cin >> data)//建立链表
    {
        node* q = new node();
        q->data = data;
        q->next = NULL;
        p->next = q;
        p = q;
    }

    //初始化f和e链表
    for(int i=0;i<10;++i)
    {
        f[i] = new node();
        e[i] = new node();
        f[i]->next = NULL;
        e[i]->next = f[i];
    }
    //排序
    RadixSort(header);  
    //输出排序结果
    p = header->next;
    while(p)
    {
        cout<<p->data<<' ';
        p = p->next;
    }
    cout<<endl;
}

上诉算法是按照严蔚敏《数据结构》书上,链式基数排序的思路实现的。
具体过程是一种最低位优先(LSD)方法:
1.先按低位关键字,分配,在排序收集。
2.接着按照关键字的优先级不断的分配收集实现最终的排序。

算法特性:
1.基数排序是稳定的,在分配收集的过程中不影响相同元素的相对位置。
2.时间复杂度O(d(n+rd)),d是关键字的个数,rd关键字的取值范围。
3.空间复杂度O(rd)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值