排序算法之shell,归并,快排

  1. 快速排序

快速排序可能是应用的最为广泛的一种算法,它流行的原因是实现简单,适用于各种不同的输入数据且在一般的应用中比其他排序算法都要快的多。快速排序的优点:
是原地排序(只需要一个很小的辅助栈)。
所需时间跟NlgN成正比。
快速排序思路:

快速排序和归并排序是互补的,归并排序将整个数组分成小数组,然后将排好序的小数组归并以将整个数组排序;而快速排序是在将大数组分成小数组的时候排序,当小数组小到不可再分的时候,排序也就完成了。
1.首先选择一个中间元素(一般选左端或者右端)。
2.分别获取除中间元素外的左右两端的索引。
3.由左右两端逐渐向中间迭代,每迭代一步比较一下索引中的元素和中间元素,当左边出现比中间元素大的元素的时候,暂停左边的迭代,当右边迭代出比中间元素小的元素的时候,右边迭代也暂停,交换左右两边的元素。
4.重复步骤3,直到左右两边的索引相遇,然后将中间元素移动到中间,这时中间元素左边的元素都比它小,右边的元素都比它大。
5.将上面的中间元素左右两边当成两个数组,分别进行上述过程。
6.重复以上步骤直到数组不可再分。

void CCelerityDlg::CelerityRun(int left, int right)
{
    int i,j; 
    int middle,iTemp; 
    i = left; 
    j = right; 
    middle = num[(left+right)/2]; //求中间值 
    do
    { 
        while((num[i]<middle) && (i<right))//从左找小于中值的数 
        {
            i++;   
        }
        while((num[j]>middle) && (j>left))  //从右找大于中值的数 
        {
            j--; 
        }
        if(i<=j)//找到了一对值 
        { 
            iTemp = num[i]; 
            num[i] = num[j]; 
            num[j] = iTemp; 
            i++; 
            j--; 
        } 
    }while(i<=j);//如果两边的下标交错,就停止(完成一次) 

    //递归左半边 
    if(left<j) 
    {
        CelerityRun(left,j); 
    }
    //递归右半边 
    if(right>i) 
    {
        CelerityRun(i,right); 
    }
}
  1. 归并排序
    法归并排序:也是递归(迭代)、合并排序。主要是经过两步,将一组元素分解成多个子序列,然后使用递归或者迭代的方式合并成一个整体序列,在合并的过程对每个子序列进行排序。

将两个已经排序好的数组归并为一个数组这一操作对于归并排序的意义不言而喻,以下是归并方法的实现:
实现一次归并排序:

void CMergeDlg::merge(int r[], int s[], int x1, int x2, int x3)
{
    int i, j, k;
    i = x1;                                         //第一部分的开始位置
    j = x2 + 1;                                         //第二部分的开始位置
    k = x1;
    while ((i <= x2) && (j <= x3))      //当i和j都在两个要合并的部分中
    {

        if (r[i] <= r[j])                   //筛选两部分中较小的元素放到数组s中
        {
            s[k] = r[i];
            i++;
            k++;
        }
        else
        {
            s[k] = r[j];
            j++;
            k++;
        }
    }
    while (i <= x2) //将x1~x2范围内的未比较的数顺次加到数组r中
    {
        s[k++] = r[i++];
    }
    while (j <= x3) //将x2+1~x3范围内的未比较的数顺次加到数组r中
    {
        s[k++] = r[j++];
    }
}

自定义函数merge_sort实现归并排序

void CMergeDlg::merge_sort(int r[], int s[], int m, int n)
{
    int p;
    int t[20];
    if (m == n)
    {
        s[m] = r[m];
    }
    else
    {
        p = (m + n) / 2;
        //递归调用merge_sort函数将r[m]~r[p]归并成有序的t[m]~t[p]
        merge_sort(r, t, m, p);
        //递归调用merge_sort函数将r[p/ +1]~r[n]归并成有序的t[p+1]~t[n]
        merge_sort(r, t, p + 1, n); 
        //调用函数将前两部分归并到s[m]~s[n]
        merge(t, s, m, p, n);       
    }
}

3.shell排序

Shell排序是插入排序的变种,因D.L.Shell提出而得名。Shell排序对数据进行分组,然后对每一组数据进行排序,在每一组数据都有序之后再对所有分组利用插入排序进行最后一次排序,这样能减少数据交换次数,加快排序速度。

  1. 确定分组规则,一般根据排序数组长度每次折半,但是必须要保证最后一次分组的间隔为1
  2. 分完组后,就是对每一组内部进行排序
    3.重复上述过程,直到进行完间隔为1(也就是插入排序)的排序。此时数组排序完成

复杂度:

希尔排序的时间是不稳定的,受分组的选择和数据的排列影响很大。
一个好的增量排序需要遵循以下原则:

1.最后一个增量必须为1
2.应尽量避免增量之间互为倍数,因此我在实现的时候增加了一个奇偶判断,如果为gap偶数,则减一使其成为奇数。

Shell排序的时间复杂度计算起来比较复杂,貌似也没有数学上对于Shell排序效率的结论,这里不做描述,有人通过大量的实验给出当n较大时,时间复杂度在O(n(3/2))到O(n(7/6))之间。

Shell排序在n特别大,也就是序列特别大并且十分无序时时间性能明显优于插入排序,原因如下:

1.大间隔的交换导致需要挪动的数据稀少,并且效率很高,最大的交换间隔可以达到n/2。
2.每次分组排序后数组就越来越趋于有序,数据离它正确的位置越来越近,这样下次的交换数据次数会明显减少。

代码实现:

void CXierOrderDlg::OnButorder() 
{
    // TODO: Add your control notification handler code here
    UpdateData(FALSE);
    shsort(num,10);
    int i, j, d;
    d = n/2;                                            //确定固定增量值
    while (d >= 1)
    {
        for (i = d; i < n; i++)                             //数组下标从d+1开始进行直接插入排序
        {
            num[0] = num[i];                                    //设置监视哨
            j = i - d;                                      //确定要进行比较的元素的最右边位置
            while ((j > 0) && (num[0] < num[j]))
            {
                num[j + d] = num[j];                                //数据右移
                j = j - d;                                  //向左移d个位置
            }
            num[j + d] = num[0];                                    //在确定的位置插入s[i]
        }
        d = d / 2;                                      //增量变为原来的一半
    }
    for (i=1;i<n;i++)                                   //循环输出数组元素
    {
        CString str;
        str.Format("%d  ",num[i]);          //格式化字符串
        m_Edit += str;
        if (i==5)                           //5个数一行
        {
            m_Edit += "\r\n";                   //输出换行符
        }
    }
    UpdateData(FALSE);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值