简单排序

对数据排序有可能是检索的一个初始步骤。本次介绍几个简单的排序算法,执行速度也相对慢些,但仍值得学习。在某些情况下,比那些复杂的算法实际上还要好一些,如对于小规模的文件以及基本有序的文件,插入排序能比快速排序算法更为有效。实际上,插入排序通常也作为快速排序算法实现的一部分。下面要介绍的三个算法都包括如下两个步骤:
1、比较两个数据项。
2、交换两个数据项,或复制其中一个数据项(插入排序)。
一、冒泡排序
冒泡排序也许是我们最早接触的一种排序算法,此算法虽然运行起来非常慢,但在概念上是最简单的。
假设有N个数据:

第一趟:d0与d1比较,如果d0>d1,则两个交换位置,然后d1到了最左边,d0与d2比较,以此类推;反之,d0与d1位置不变,d1与d2再比较,以此类推。这样一趟下来,最大的数就跑到了最右边dN-1的位置。这一趟共进行了N-1次比较和[0,N-1]次交换。
第二趟:按照第一趟同样的规则进行比较和交换,不过这次不用比较最右边的dN-1,因为它已经是排过序的了,这一趟总共进行了N-2次比较和[0,N-2]次交换。
以此类推... ...
直到第N-1趟,这一趟只剩下d0和d1位置的两个数据了,比较1次,交换[0,1]次。
这样N-1趟比较和交换完之后,这组数据就有序了。在这次排序中,N个数据总共进行了N*(N-1)/2次比较和平均N*N/4次交换。
算法:
for(int out = N-1; out > 0, out--)
 {
  for(int in = 0; in < out; in++)
  {
   if(a[in] > a[in+1])
   {
    int temp =a[in];
    a[in] = a[in+1];
    a[in+1] = temp;
   }
  }
 }
数据交换部分可以单独写在一个函数中,考虑到函数会增加额外的开销,因此,没有单独的拿出来。
冒泡排序需要O(N*N)运行时间级,无论如何,只要一看到循环嵌套在另一个循环里,就可以怀疑这个算法的运行时间级为O(N*N)。
二、选择排序
选择排序改进了冒泡排序,交换次数由O(N*N)减少到O(N),但是比较次数并没有减少,与冒泡排序相同。
假设还是上面的N个数据。
第一趟:out=d0,假设d0是最小的,min = out;然后min与d1比较,若min<d1,然后min与d2比较,以此类推;反之如果min>d1,将d1赋给min,然后用min与d2比较,以此类推,直到dn-1。这一趟比较完之后,把最小的放在d0位置,即与out交换。这样d0位置的数据就是有序的了。这次共进行了N-1次比较和[0,1]交换。
第二趟:按照第一趟的规则比较和交换,最后d0和d1的位置数据是有序的。这次共进行了N-2次比较和[0,1]次交换。
以此类推 ... ...
直到第N-1趟,dn-2和dn-1位置的数据进行比较,若dn-2>dn-1,则交换;否则不交换,这样这个数据就有序啦,最后一趟进行了1次比较和[0,1]次交换。
算法:
 int min;
 for(int out = 0; out < N-1, out++)
 {
  min = out;
  for(int in = out+1; in < N; in++)
  {
   if(a[in] < min)
    min = a[in];
  }
  int temp = out;
  out = min;
  min = out;
 }
三、插入排序
一般情况下,插入排序比冒泡排序快一倍,比选择排序还要快一点,它经常被用在较复杂的排序算法最后阶段,如快速排序。直接插入排序是将无序的数据一个一个的插入到有序的数据中。当前元素的前面元素均为有序,要插入时,将当前元素位置空出,从当前元素的左边一个数据开始往前找(从后往前找),比当前元素大的元素均往右移一个位置,最后把当前元素放在它应该呆的位置就行了。刚开始的数据,我们是无法直接确定哪个元素为有序无序的界限,因此,把这个界限放在前两个数据之间。也就是下图的虚线左侧为有序数据,右侧为无序数据。

第一趟:当前元素d1放到一个临时变量中,当前元素d1与d0比较,若d0>d1,则d0右移,d1的数据放到d0位置;反之,d0与d1位置的数据都不动,虚线就到了d1与d2之间,此时,d0与d1位置的数据都是有序数据。这一趟总共比较了1次,移动了[0,1]次。
第二趟:这趟开始时,当前元素为d2,按照第一趟同样的规则,d2放到临时变量中,临时变量中的数据先与d1比较,如果d1>d2,d1右移到d2位置,然后临时变量的数据与d0比较,若d2>d0,则把临时变量中的数据放到d1右移空出来的位置,若临时变量数据<d0位置的数据,d0右移到d1空出的位置,然后临时变量中的数据放到d0空出的位置。这一趟总共进行了[1,2]次排序和[0,2]次移动。
以此类推... ...
第N-1趟:此时虚线已经到了dN-2与dN-1位置之间,已经有N-1个数据有序了,最后只要把最后一个位置dN-1的数据插入到已有序的数据序列中就ok啦。这趟进行了[1,N-1]次比较和[0,N-1]次移动。
第一趟排序中最多比较一次,第二趟最多比较两次,以此类推,最后一趟最多比较N-1次。因此总共比较的次数在区间[N-1,N*(N-1)/2]中,复制的次数在区间[0,N*(N-1)/2]中,平均N*(N-1)/4次,由于一次复制和一次交换的时间耗费不同,所以相对于随机数据,这个算法比冒泡排序快一倍,比选择排序略快。
算法:
 for(int out = 1; out < N, out++)
 {
  temp = out;
  in = out;
  while(in > 0 && a[in-1] > temp)
  {
   a[in] = a[in-1];
   --in;
  }
  a[in] = temp;
 }
可以把内层while循环用for循环表示:
 for(int out = 1; out < N, out++)
 {
  temp = out;
  for(in = out; in > 0; --in)
  {
   if(a[in-1] > temp)
    a[in] = a[in-1];
  }
  a[in] = temp;
 }
插入排序对于基本有序的文件进行排序,是一个简单有效的方法。
四、对象排序
以对象的某一属性为关键字来排序,下面以插入排序来对对象进行排序。
算法:
 int in,out;
 for(out = 1; out < N; out++)
 {
  Person temp = a[out];
  in = out;
  while(in > 0 && a[in-1].getLastName().CompareTo(temp.getLastName()) > 0)
  {
   a[in] = a[in-1];
   --in;
  }
  a[in] = temp;
 }
s1.CompareTo(s2):其实就是依次比较两个字符串的ASCII码,若两个字符的ASCII码相同则继续比较,否则直接返回两个ASCII码的差值。
稳定性:如果具有相同关键字的数据项,经过排序它们的顺序保持不变,这样的排序就是稳定的。
大多数情况下, 假设当数据量比较小或基本上有序时,插入排序算法是三种简单排序算法中最好的选择,对于更大数据量的排序来说,快速排序通常是最快的方法。以上三种简单排序的时间复杂度都是O(N*N)。
衡量算法除了比较速度,还要看内存空间的占用上。三个算法除了需要初始数组之外,都只需要一个临时变量。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值