排序算法小结(2)选择排序

接着上一篇的写。
1.选择排序-简单选择排序
看似简单的问题,自己弄了好久发现了很多问题,觉得自己对基本算法的思路,掌握的还不是很牢固。上代码:

int getMinIndex(int* a,int b,int e)
{//找出最小值
    if(a==NULL || b > e)
        return -1;
    int minInd = 0;
    for(int i=b;i<e;++i)
    {
        if(a[minInd] > a[i])
        {
            minInd = i;
        }
    }
    return minInd;
}

void SimpleSelectSort(int* a,int n)
{//简单选择排序
    if(a == NULL || n == 0)
        return ;
    for(int i=0;i<n;++i)
    {
        int j = getMinIndex(a,i,n);//问题出现在这里,其实找到对应的值之后,有很
        //多种做法,可以借用插入排序的思路,移位,调整待排序列(我最开始就是用的这种
        //方法,但是这种方法的时间复杂度明显比较高);接着就是可以用下面的这种交换排
        //序的思想,O(1)的时间复杂度。(看来考虑问题时的思路有些时候真的太局限)
        if(j != i)//最开始以为这个判断没必要,其实时十分有必要的,如果元素基本有
        //序,加入这个判断可是可以减少很多次交换操作的哟。
        {
            int tmp = a[j];
            a[j] = a[i];
            a[i] = tmp; 
        }       
    }
}

算法思路:每次选择一个最小(大)的数,与有序序列后面的一个元素(第一个不有序的元素)做交换
可以看出本算法:
1.简单排序算法是不稳定的。这个有点不好直接看出来,因为寻在最小值时是依次比较的,这个过程不会影响算法的稳定性,但是在交换时,由于交换位置的不确定性,会导致算法出现不稳定的情况:如{3*,3,1}–>{1,3,3*}。总之简单选择排序是不稳定的。最有一点,如果采用插入方式替换交换,那么这个算法就是稳定得了,可惜时间复杂度会上去。
2.时间复杂度,这个明显会一直是O(n^2),两个循环会一直执行。
3.空间复杂度,O(1)保持最小或最大值.

2选择排序–简单选择排序的改进(二元选择排序)

改进并没有降低时间复杂度和空间复杂度。但是却提供了一个很好的思路:类似与多线程解决问题的方法。在每次循环时找到一个最小值和最大值分别放到对应的位置有序位置上,两边同时排序。这样可以把最外层的循环由n降至n/2。
这种方法在提高效率的同时,也带来了一些问题:就像多线程一下,如果同时访问一个位置数据可能就会出现问题。当要跟最小值交换的元素位置上刚好是最大值时,需要进行特殊的处理。当然也不是很难,看代码吧:

void getMinMaxIndex(int* a,int b,int e,int& minInd,int& maxInd)
{//一次查找最小值和最大值
    if(a == NULL || e < b)
        return ;    
    minInd = b;
    maxInd = b;
    for(int i=b;i<=e;++i)
    {
        if(a[minInd] > a[i])
        {
            minInd = i;
            continue;
        }
        if(a[maxInd] < a[i])
        {
            maxInd = i;
        }
    }
}
void BinarySSS(int* a,int n)
{
    if(a == NULL || n == 0)
        return;
    int begin=0,end=n-1;
    while(begin < end)
    {
        int minInd,maxInd;
        getMinMaxIndex(a,begin,end,minInd,maxInd);
        int tmp;        
        if(begin != minInd)
        {//最小值是否在begin位置上,不是先交换
            tmp = a[begin];
            a[begin] = a[minInd];
            a[minInd] = tmp;
        }   
        if(maxInd == begin)//最大值是不是在being位置上,如果在说明进过前次交换最
        //大值已经位于minInd位置,因此需要修改maxInd。
            maxInd = minInd;
        if(maxInd != end)   
        {//判断最大值是否需要交换
            tmp = a[end]; 
            a[end] = a[maxInd];
            a[maxInd] = tmp;
        }   
        begin++;
        end--;
    }
    display(a,n);
}

关与这个问题,有一篇专门的博客探讨http://blog.csdn.net/ye_scofield/article/details/39312717

2选择排序–堆排序
堆排序比较复杂,借助完全二叉树的数组存储形式来实现。整个排序包含三个部分:调整堆(adjust),构建堆(makeHeap),排序输出(HeapSort)。算法实现:

void adjust(int*a ,int pos,int len)
{
    //堆的一次调整,自上而下:从pos节点开始向下调整成堆结构
    if(a == NULL || pos > len || len == 0)
        return;
    int value = a[pos];
    int child = pos * 2+1;//找到左孩子节点
    while(child < len)
    {
        if(child+1<len && a[child] > a[child+1])
            ++child;//有右孩子,且右孩子节点值较小
        if(a[pos] > a[child])
        {
        //根节点大于孩子节点交换
            a[pos] = a[child];
            pos = child;
            child = pos*2+1;
        }
        else break;
    }
    a[pos] = value;//将值放置于合适的位置
    display(a,len);
}
void makeHeap(int* a,int len)
{
    if(a == NULL || len == 0)
        return;
    for(int i=len/2;i>=0;--i)//从第一个非叶子节点开始向上依次调用adjust()。
        adjust(a,i,len);
}
void heapSort(int *a,int len)
{
    //建堆
    makeHeap(a,len);
    //排序,每次取出堆顶数据放于,堆的最后一个位置,并缩小堆的规模
    for(int i=len-1;i>=0;--i)
    {
        //交换堆顶与最后一个元素,从新调整堆
        int tmp = a[0];
        a[0] = a[i];
        a[i] = tmp;
        adjust(a,0,i);
    }
}

算法思路:
1.adjust(),调整数组使之成为堆结构。按照堆的性质,自上而下的比较交换。保证pos一下的节点具有堆的性质。
2.makeHeap(),创建堆。从堆中第一位非叶子节点开始向上调整堆,使整个数组具有堆的结构。
3.HeapSort(),堆排序。每次取出最小一个节点放置于堆结构的最后,并缩小堆的规模,最终输出相反排序的数组(大顶堆,结果从小到大排序;小顶堆,结果从大到小排序)。

从以上描述可以看出:
1.算法不稳定,属于选择排序,很明显无法保证,数字的相对位置关系。
2.时间复杂度为O(nlog2n);
3.空间复杂度为O(1).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值