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

news/2024/5/17 18:26:27 标签: 排序算法, 快速排序, 归并排序, 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);
}

http://www.niftyadmin.cn/n/496448.html

相关文章

中小企业的四个数据存储方法和措施

“每家企业都是一家科技公司”&#xff0c;随着大数据的增长&#xff0c;这个说法最近被抛在脑后&#xff0c;但这是真实的。各种规模的公司&#xff0c;从成长中的企业&#xff0c;政府机构&#xff0c;社会企业和非营利组织&#xff0c;正在面临编制数据和保护数据的新现实&a…

“自我管理”的第一步:看清自己

像拿破仑、达芬奇、莫扎特这样的伟人都是自己管理自己的。这在很大程度上也是他们功成名就的源泉。今天&#xff0c;越来越多的劳动者和大多数知识工作者面临着“自我管理”的挑战&#xff0c;需要学会如何发展自己&#xff0c;学会选择适当的方法和时机改变他们所做的工作以及…

Network Policy - 每天5分钟玩转 Docker 容器技术(171)

Network Policy - 每天5分钟玩转 Docker 容器技术&#xff08;171&#xff09; CloudMan 2018-05-26 第171篇 Network Policy Network Policy 是 Kubernetes 的一种资源。Network Policy 通过 Label 选择 Pod&#xff0c;并指定其他 Pod 或外界如何与这些 Pod 通信。 默认…

Java与C++如何处理循环引用问题

最近刷题刚刚研究过这个问题。 何为循环引用 如果有两个或者以上的对象&#xff0c;它们彼此引用&#xff0c;就会造成循环引用。如下面的例子 class Node { Node next ; } Node a new Node (); Node b new Node (); a . next b ; b . next a ; 代码中&#xff0…

Leetcode 338. Counting Bits

Question Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1s in their binary representation and return them as an array.Example For num 5 you should return [0,1,1,2,1,2].Follow up It is very…

第九章 组织中的职权配置

一.概念1.职权:是经由一定的正式程度赋予某一职位的一种权力.2.直线职权:是某项职位或某部门所拥有的包括作出决策,发布命令等的权力,也就是通常说的指挥权.3.参谋职权:是某项职位或部门所拥有的辅助性职权,包括提供咨询,建议等4.职能职权:是某职位或某部门所拥有的原属于直线管…

实践 Network Policy - 每天5分钟玩转 Docker 容器技术(172)

实践 Network Policy - 每天5分钟玩转 Docker 容器技术&#xff08;172&#xff09; 原创 CloudMan CloudMan 2018-05-25 第172篇 实践 Network Policy 为了演示 Network Policy&#xff0c;我们先部署一个 httpd 应用&#xff0c;其配置文件 httpd.yaml 为&#xff1a; htt…

追女人必知女人的24个薄弱环节(zt)

追女人必知女人的24个薄弱环节第一 称呼 女性对男性称呼由浓而淡分别是亲爱的-你-您&#xff0c;见面时对她说“我以后可以称您为你吗”&#xff0c;也许她一时会感到莫名奇妙&#xff0c;但这却是一种可以刺激女性爱意的用语。第二 理论 女性在理论方面的能力普遍较弱&#xf…