排序算法-------交换排序


1. 冒泡排序

冒泡排序之前已经熟悉其过程,不在画图,直接上代码

void Bubble(DataType* a,size_t n)
{
    assert(a != NULL && n > 0);
    for(int end = n;end > 0;--end)
    {
        int flag = 0;
        for(int i = 1;i < end;++i)
        {
            if(a[i-1] > a[i])
            {
                Swap(&a[i-1],&a[i]);
                flag=1;
            }
        }
        if(flag == 0)
        {
            break;
        }
    }   
}

2.快速排序

三种方法

1.左右指针法

(1)从数组当中选取一个关键元素key;

这里写图片描述

(2)begin在数组最左边,从数组的左边走,直到找到比key大的数
(3)end在数组最右边,从右向左走,直到找到比key小的数
(4)交换begin和end所在位置的值

这里写图片描述

(5)当begin和end走到相同位置时,交换关键元素key和begin位置的元素

这里写图片描述

(6)此时一趟排序完成,以begin为分界线,划分为左区间和右区间,在进行左区间和右区间的排序
(7)当左区间右区间任意一个不存在或者只剩一个元素时,排序即可完成

代码:

//左右指针法
int PartSort1(DataType* a1, int left, int right)
{
    assert(a);
    DataType key = a[right];
    int begin = left, end = right;

    while (begin < end)
    {
        //选出比key大的数据
        while (begin < end && a[begin] <= key)
            ++begin;

        //选出比key小的数
        while (begin < end && a[end] >= key)
            --end;

        //交换begin和end两个位置的值
        Swap(&a[begin], &a[end]);
    }

    //注意这里是换a[right]和a[begin]的值
    Swap(&a[begin], &a[right]);
    return begin;
}

//快速排序
void QuickSort(DataType* a, int left, int right)
{
    assert(a);

    if (right - left > 10)
    {
        InsertSort(a, right - left + 1);
    }
    else
    {
        //划分区间返回条件,当左区间或右区间不存在,或只有一个数时,开始返回
        if (left >= right)
            return;
        int div = PartSort1(a, left, right);

        //去左区间排序
        QuickSort(a, left, div - 1);
        //去右区间排序
        QuickSort(a, div + 1, right);


    }
}

2.挖坑法

(1)从数组当中选取一个关键元素key,以key的位置作为第一个坑index

这里写图片描述

(2)begin在数组最左边,从数组的左边走,直到找到比key大的数,将begin位置的元素填入坑,a[index]=a[begin],此时begin作为新的坑,index=begin

这里写图片描述

(3)end在数组最右边,从右向左走,直到找到比key小的数a[end],将end的元素给坑index,a[index]=a[end],此时end作为新的坑

这里写图片描述

(4)当begin和end走到相同位置,此时坑就在begin位置,将key填入坑
这里写图片描述

(5)此时一趟排序完成,以begin为分界线,划分为左区间和右区间,在进行左区间和右区间的排序
(6)当左区间右区间任意一个不存在或者只剩一个元素时,排序即可完成

代码:

//填坑法
int PartSort2(DataType* a1, int left, int right)
{
    assert(a);
    DataType key = a[right];


    int begin = left, end = right;

    while (begin < end)
    {
        //坑在end,找到比key大的值,将数据填入坑
        while (begin < end && a[begin] <= key)
            ++begin;
        a[end] = a[begin];

        //此时坑在begin处,找到比key小的值,将数据填入坑
        while (begin < end && a[end] >= key)
            --end;
        a[begin] = a[end];
    }
    //当begin和end在同一位置处,将key填在坑处
    a[begin] = key;
    return begin;
}

//快速排序
void QuickSort(DataType* a, int left, int right)
{
    assert(a);

    if (right - left > 10)
    {
        InsertSort(a, right - left + 1);
    }
    else
    {
        //划分区间返回条件,当左区间或右区间不存在,或只有一个数时,开始返回
        if (left >= right)
            return;
        int div = PartSort2(a, left, right);

        //去左区间排序
        QuickSort(a, left, div - 1);
        //去右区间排序
        QuickSort(a, div + 1, right);


    }
}

3.前后指针法

(1)选取一个关键元素可以,并定义两个指针,int cur=left;int prev=cur-1;

这里写图片描述

(2)a[cur] >= key,cur++

这里写图片描述

(3)a[cur] < key,cur停下来,prev++,如果prev != cur,则交换两位置的数值;

这里写图片描述

这里写图片描述

(4)当cur走到数组的最后,表示一趟已经结束,将cur的值和prev的后一位置进行交换

这里写图片描述

(5)以prev为界限,划分为左区间和右区间
(6)当左区间或右区间不存在,或只有一个数时,排序即完成

代码

//前后指针
int PartSort3(DataType* a1, int left, int right)
{
    assert(a);
    DataType key = a[right];
    int cur = left;
    int prev = left - 1;

    while (cur < right)
    {

        //当a[cur]比key小,prev走,并且当prev不在同一位置上时,进行交换
        if ( a[cur] < key)
        {
            ++prev;
            if (cur!=prev)
                Swap(&a[cur], &a[prev]);
        }

        //不管比key小还是大,cur继续向后走
        ++cur;
    }
    //当cur走到数组的最后,表示一趟已经结束,将cur的值和prev的后一位置进行交换
    Swap(&a[++prev], &a[right]);
    return prev;//以prev为界限,划分为左区间和右区间
}


//快速排序
void QuickSort(DataType* a1, int left, int right)
{
    assert(a);

    if (right - left > 10)
    {
        InsertSort(a, right - left + 1);
    }
    else
    {
        //划分区间返回条件,当左区间或右区间不存在,或只有一个数时,开始返回
        if (left >= right)
            return;
        int div = PartSort2(a, left, right);

        //去左区间排序
        QuickSort(a, left, div - 1);
        //去右区间排序
        QuickSort(a, div + 1, right);
    }
}

快速排序的优化

1. 小区间优化

当区间中元素数量较小时在用递归反而会使效率降低,当区间元素数据较小,就越接近排序,此时,我们采用直接插入排序进一步提高效率

2. 随机值法

在选key时,在数组中随机选取一个数作为key进行排序;

代码:

sand(time(0));
int  index = rand()%(right - left + 1);//随机产生下标
int   key = a[index];

注意:这种方法并不实用,随机产生的key是大还是小并不确定,key选取的不合适反而会降低效率

3. 三数取中法

 选取数组的首元素a[0]、尾元素a[n-1]以及中间元素a[(0+n-1)/2]选出三者的中位数作为key值。这时候选出来的key可以保证绝对不是最大数或最小数(如果三个数都是相同的,那key还是最大或最小值,不过出现这种情况概率及其低)

int Getkey(DataType* a, int left, int right)
{

    assert(a);
    DataType mid = left + ((right - left) >> 1);

    // mid恰好为key
    //情况1:left <mid ,mid <right
    //情况2:left>mid,mid > right
    if ((a[left] < a[mid] && a[mid] < a[right])
        || (a[right] < a[mid] && a[mid] < a[left]))
    {
        return mid;
    }

    //left为key
    //情况1:left > right,left < mid
    //情况2:left >mid, left < right
    else if ((a[right] < a[left] && a[left] < a[mid])
        || (a[mid] < a[left] && a[left] < a[right]))
    {
        return left;
    }
    else
    {
        //right为key
        //情况1:right > left ,right < mid
        //情况2:right < left,right >mid
        return right;
    }
}

快速排序的非递归

递归是依赖于函数栈帧,那么我们可以用我们的栈去模拟实现函数的递归调用

void QuickSortNonR(DataType* a, int left, int right)
{
    assert(a);
    Stack s;
    StackInit(&s);

    //先将左右区间进行入栈
    StackPush(&s, left);
    StackPush(&s, right);

    while (left < right && StackEmpty(&s) != 0)
    {
        //从栈中取出区间坐标
        int end = StackTop(&s);
        StackPop(&s);
        int begin = StackTop(&s);
        StackPop(&s);

        //计算下一次区间的临界值
        int div = PartSort1(a, begin, end);

        //如果区间内有两个数以上,进行入栈
        if (begin < div-1 )
        {
            StackPush(&s, begin);
            StackPush(&s, div-1 );
        }

        if (div+1  < end)
        {
            StackPush(&s, div+1 );
            StackPush(&s, end);
        }

    }
}

两种算法分析
稳定性
  • 冒泡排序:稳定算法,在单趟排序和多趟排序的过程中,元素的相对位置都不会发生变化
  • 快速排序:不稳定算法,三种方法实现过程中都存在元素交换,相对位置会发生改变
时间复杂度
  • 冒泡排序:O( n2 n 2
  • 快速排序:经优化过后为O(nlgn),最坏的情况为O( n2 n 2
空间复杂度
  • 冒泡排序:O(1)
  • 快速排序:O(lgn)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值