• 排序


    排序

    信息获取后通常需要进行处理,处理后的信息其目的是便于人们的应用。信息处理方法有多种,通常有数据的排序,查找,插入,删除,归并等操作。读者已经接触了一些这方面的知识,本章重点介绍数据排序的几种方法。

    1. 选择排序

    基本思想:每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在待排序的数列的最前,直到全部待排序的数据元素排完。

    【程序实现】
    for (i=1;i<=n;i++)           //i控制当前序列中最小值存放的数据位置
    {
      for (j=i+1;j<=n;j++)    //在当前无序区a[i..n]中选最小的元素a[i]
         if (a[j]<a[i]) swap(a[i],a[j]);
    交换a[i]a[j],将当前最小值放到a[i]位置
     }

    2. 冒泡排序

    冒泡排序的思想:以n个人站队为例,从第1个开始,依次比较相邻的两个是否逆序对(高在前,矮在后),若逆序就交换这两人,直到n-1n比较,经过一轮比较后,则把最高的人排到最后,即将最高的人像冒泡一样逐步冒到相应的位置。如此,进行n-1轮后,队列为有序的队列。

    【程序实现】
        for(i=n-1; i>=1; i--)       进行n-1轮冒泡
           for(j=1; j<=i; j++)      每轮进行i次的比较
              if(a[j]>a[j+1])       相邻两个元素比较,若逆序就交换
                swap(a[j], a[j+1]);  交换

    3. 插入排序

         插入排序思想:当读入一个元素时,在已经排序好的序列中,搜寻它正确的位置,再放入读入的元素。      

    【程序实现】

        for(i=1; i<=n; i++)

    {

    Scanf(“%d”,&p);

           for(j=1; j<i; j++)     在前面有序区间中为a[i]找合适的插入位置

             if (p>a[J]) break;        找到比a[i]小的位置就退出,插入其后 for(k=I+1;k>=J;k--)    将比a[i]大的数据向后移

             a[k]=a[k-1];              a[i]放在正确位置上

    A[J]=P;

        }

    4. 桶排序

    桶排序的思想是若待排序的值在一个明显有限范围内(整型)时,可设计有限个有序桶,待排序的值装入对应的桶(当然也可以装入若干个值),桶号就是待排序的值,顺序输出各桶的值,将得到有序的序列。

    【程序实现】

    for (i=1;i<=n;i++)

             cin>>k; b[k]++;         //将等于k的值全部装入第k桶中

         for (i=0;i<=100;i++)                  //输出排序结果

         { while (b[i])   cout<<i<<""; b[i]--;   }

    5.快速排序

       快速排序是对冒泡排序的一种改进。它的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

       假设待排序的序列为{a[L],a[L+1],a[L+2],……,a[R]},首先任意选取一个记录(通常可选中间一个记作为枢轴或支点),然后重新排列其余记录,将所有关键字小于它的记录都放在左子序列中,所有关键字大于它的记录都放在右子序列中。由此可以将该“支点”记录所在的位置mid作分界线,将序列分割成两个子序列和。这个过程称作一趟快速排序(或一次划分)。

       一趟快速排序的具体做法是:附设两个指针ij,它们的初值分别为LR,设枢轴记录取mid,则首先从j所指位置起向前搜索找到第一个关键字小于的mid的记录,然后从i所指位置起向后搜索,找到第一个关键字大于mid的记录,将它们互相交换,重复这两步直至i>j为止。

       快速排序的时间的复杂性是O(nlog2n),速度快,但它是不稳定的排序方法。就平均时间而言,快速排序是目前被认为是最好的一种内部排序方法

      由以上讨论可知,从时间上看,快速排序的平均性能优于前面讨论过的各种排序方法,但快速排序需一个栈空间来实现递归。若每一趟排序都将记录序列均匀地分割成长度相接近的两个子序列,则栈的最大深度为log(n+1)

    【程序实现】

    void qsort(int l,int r)

    {  

        int i,j,mid,p;

        i=l;  j=r;

        mid=a[(l+r) / 2];          将当前序列在中间位置的数定义为分隔数

        do

        {

           while (a[i]<mid) i++;  在左半部分寻找比中间数大的数

           while (a[j]>mid) j--;    在右半部分寻找比中间数小的数

           if (i<=j)

    Swap(a[i],a[j]),i++,j--;

    若找到一组与排序目标不一致的数对则交换它们,继续找

        }while (i<=j);             

        if (l<j)  qsort(l,j);      注意这里不能有等号

    if (i<r)  qsort(i,r);      若未到两个数的边界,则递归搜索左右区间

    }

    6.归并排序

       归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

    合并过程为:比较a[i]a[j]的大小,若a[i]a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令ik分别加上1;否则将第二个有序表中的元素a[j]复制到r[k]中,并令jk分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]

    【程序实现】

    void msort(int s,int t)

    {

        if(s==t) return;           如果只有一个数字则返回,无须排序

        int mid=(s+t)/2;

        msort(s,mid),msort(mid+1,t);           分解左右序列

        int i=s, j=mid+1, k=s;   接下来合并

        while(i<=mid && j<=t)

        {

              if(a[i]<=a[j])  r[k++]=a[i++];

              Else r[k++]=a[j++]ans+=mid-i+1;  统计产生逆序对的数量

        }  

        while(i<=mid)     r[k++]=a[i++];

        while(j<=t)  r[k++]=a[j++];   复制左右边子序列剩余

        for(int i=s; i<=t; i++) a[i]=r[i];  

    }

    归并排序的时间复杂度是O(nlogn),速度快。同时,归并排序是稳定的排序。即相等的元素的顺序不会改变。

    7.各种排序算法的比较

    1.稳定性比较

    插入排序、冒泡排序、二叉树排序、二路归并排序及其他线形排序是稳定的。选择排序、希尔排序、快速排序、堆排序是不稳定的。

    2.时间复杂性比较

    插入排序、冒泡排序、选择排序的时间复杂性为O(n2);快速排序、堆排序、归并排序的时间复杂性为O(nlog2n);桶排序的时间复杂性为O(n);若从最好情况考虑,则直接插入排序和冒泡排序的时间复杂度最好,为O(n),其它算法的最好情况同平均情况相同;若从最坏情况考虑,则快速排序的时间复杂度为O(n2),直接插入排序和冒泡排序虽然平均情况相同,但系数大约增加一倍,所以运行速度将降低一半,最坏情况对直接选择排序、堆排序和归并排序影响不大。

     由此可知,在最好情况下,直接插入排序和冒泡排序最快;在平均情况下,快速排序最快;在最坏情况下,堆排序和归并排序最快。

    3.辅助空间的比较

    桶排序、二路归并排序的辅助空间为O(n),快速排序的辅助空间为O(log2n),最坏情况为O(n),其它排序的辅助空间为O(1);

    4.其它比较

    插入、冒泡排序的速度较慢,但参加排序的序列局部或整体有序时,这种排序能达到较快的速度。反而在这种情况下,快速排序反而慢了。

    n较小时,对稳定性不作要求时宜用选择排序,对稳定性有要求时宜用插入或冒泡排序。

    若待排序的记录的关键字在一个明显有限范围内时,且空间允许是用桶排序。

     n较大时,关键字元素比较随机,对稳定性没要求宜用快速排序。

    n较大时,关键字元素可能出现本身是有序的,对稳定性没有要求时宜用堆排序

     快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;

    堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。

    【总结】:

      1. 考试尽量用STL的快速排序
      2. 归并排序需要掌握,解决部分问题
      3. 归并排序注意最后的数组间ab的赋值
      4. 计算逆序对的时候只需要进行nlogn的归并排序即可
      5. 对于原本已经是有序的两个集合,使用归并
      6. 桶排序的时候,非常小心时空复杂度

    感谢各位与信奥一本通的鼎力相助!

  • 相关阅读:
    谈谈node(1)
    怎么调用html5的摄像头,录音,视频?
    es6-块级作用域let 和 var的区别
    输入手机号自动分隔
    How do I know which version of Javascript I'm using?
    PHP的类中的常量,静态变量的问题。
    【转】马拉松式学习与技术人员的成长性
    JavaScript Prototype in Plain Language
    Promise编程规范
    XMLHttpRequest对象解读
  • 原文地址:https://www.cnblogs.com/SeanOcean/p/10975557.html
Copyright © 2020-2023  润新知