排序

排序

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

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. 桶排序的时候,非常小心时空复杂度

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

posted @ 2019-06-04 19:14  SeanOcean  阅读(268)  评论(1编辑  收藏  举报