交换机数据预处理系统之数据排序分析

来源:互联网 发布:淘宝发布宝贝多种口味 编辑:程序博客网 时间:2024/06/09 17:29
       排序对于处理大型的数据时,是一件比较麻烦的事,特别是对于海量数据的排序,这时选择合适的排序算法是十分重要的,比如冒泡和选择排序来说,它们的时间复杂度都是On2),它的效率对于百条以内的数据是十分有效的,但是一旦数据达到了上千条,甚至上万条,百万条。这时还使用此类排序算法显然是很不明智的。

      那么,我们应当选择何种排序算法呢?数据结构中给我们提供了很多的排序算法,我们该使用哪一种呢?我们在这里做一个简单的分析,看哪种排序更加适合。下面给出了一些常见的排序算法及其时间复杂度。

         插入排序 O(n2)

  冒泡排序 O(n2)

  选择排序 O(n2)

  快速排序 O(n log n)

  堆排序 O(n log n)

  归并排序 O(n log n)

  基数排序 O(n)

         希尔排序 O(n1.25)

插入和冒泡、选择我们就不做考虑了,现在主要分析后面的几种排序算法。

快速排序是所有排序算法中最高效的一种。它采用了分治的思想:先保证列表的前半部分都小于后半部分,然后分别对前半部分和后半部分排序,这样整个列表就有序了。这是一种先进的思想,也是它高效的原因。因为在排序算法中,算法的高效与否与列表中数字间的比较次数有直接的关系,而"保证列表的前半部分都小于后半部分"就使得前半部分的任何一个数从此以后都不再跟后半部分的数进行比较了,大大减少了数字间不必要的比较。但查找数据得另当别论了。

但是,快排存在一个问题,那就是实现函数的递归调用,有人也许就会问了,这和递归又有什么关系呢?不好意识,有关系而且关系很大,因为递归调用是使用的是栈空间,而内存中的栈空间的大小是有限的。根据实践经验(当然是一些技术强人的实验结果)一旦递归超过8000次以上,就会出现栈空间溢出的问题。而对于我们动辄就上万条数据来说,选择快排并非是明智之举。

堆排序与前面的算法都不同,它是这样的:

   堆排和快排都使用了分治的思想。

首先新建一个空列表,作用与插入排序中的"有序列表"相同。

  找到数列中最大的数字,将其加在"有序列表"的末尾,并将其从原数列中删除。

  重复2号步骤,直至原数列为空。

  堆排序的平均时间复杂度为nlogn,效率高(因为有堆这种数据结构以及它奇妙的特征,使得"找到数列中最大的数字"这样的操作只需要O(1)的时间复杂度,维护需要logn的时间复杂度),但是实现相对复杂(可以说是这里7种算法中比较难实现的)。

看起来似乎堆排序与插入排序有些相像,但他们其实是本质不同的算法。至少,他们的时间复杂度差了一个数量级,一个是平方级的,一个是对数级的。

归并排序是将两个或两个以上的有序子表合并成一个新的有序表。初始时,把含有n个结点的待排序序列看作由n个长度都为1的有序子表组成,将它们依次两两归并得到长度为2的若干有序子表,再对它们两两合并。直到得到长度为n的有序表,排序结束。
    归并排序是一种稳定的排序,可用顺序存储结构,也易于在链表上实现,对长度为n的文件,需进行log2n趟二路归并,每趟归并的时间为O(n),故其时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlog2n)。归并排序需要一个辅助向量来暂存两个有序子文件归并的结果,故其辅助空间复杂度为O(n),显然它不是就地排序。它是以空间来换时间的排序算法,如果数据的条数过于庞大,那辅助空间就不可预计了,所以不建议使用此中排序方法。

呵呵,由于本人对于后面的两种排序算法不是很了解,在此就不做分析了。我们就选择性能比较稳定的堆排序来做分析!

让我们正式开始吧!

首先,我们知道计算机的数据存储在磁盘上是以01代码进行存储的。那么以什么方式打开此文件那是由程序员决定的,比如对于字母a来说,我们以字符进行解读那它就是a,如果以整形来进行解读那它就是97了,好知道这个后,问题就变得十分简单了,我们可以先对文件中的记录读入内存,然后在内存中进行解读。

01  1234584  01  123         01:00:28  01:00:33 $2 000004 0001 0002

01  5645464  01  16450540    01:00:53  01:01:57 1996/09/06 000063 0002 0002

01  8754545  01  165465401   01:09:53  01:12:10 1996/09/06 000136 0003 0002

01  2721241  01  123         01:12:50  01:32:26 1996/09/06 001176 0020 0002

01  2656232  01  7254620     10:11:39  10:14:48 1996/09/06 000189 0008 0002

01  2721168  01  0878526     10:31:31  10:31:48 1996/09/06 000017 0002 0003

01  2721168  01  5854465     10:55:19  10:59:55 1996/09/06 000276 0010 0002

我们现在要做的就是猜想此文件的结构了,你可以把它全部当成int来处理,也可以把它当做char型来处理。那我们先分析一下这两种解析方式的利弊吧。

我们把他们当做int来进行处理,无非是上面的数据看上去都是int类型的数据,这也无可厚非的,但是,从第四列的数据你就可以看出一个问题,那就是数据长度不一致?我们知道int类型的数据是有长度限制的,int最长不能超过231-1unsigned int最长不能超过232,而一旦里面出现一个超过它的表示范围的,那就会造成数据丢失,如果此项是银行存款的话,那这里的损失无疑是不可计量的。

那我们分析一下char类型的好处吧,首先,它没有长度的限制,我无非是多申请一些空间而已,还有就是排序是需要数据间的对比的,C语言中对于字符串提供了大量的库函数让我们使用,大大减轻了程序员的负担。

我们从使用空间上来分析一下,我们知道,int类型是占4个字节,而char只占1个字节,从第13567这三列的数据来看,使用char使用int能节省更多的内存空间。

好了,有了上面的分析,我们是不是已经猜想出了此文件的结构了?那就定义此结构吧!

现在,让我们开始进行文件的操作(呵呵,核心哦!)

我们先利用fgets读一条记录到内存中,众所周知,fgets是遇到换行结束的函数,我们利用此函数,可以方便快速的读出文件中的记录,然后先把它存放到一个字符数组中。

然后,我们利用C标准库中得strtok函数,分别截取其中的每一项,并把它存放到结构体数组中,至于具体怎么做,我想大家都比较了,在此就不做叙述了。

最后是,我们把文件中的数据都取出了,并且正确的存放到了结构体数组中了,现在开始进行排序了,我们先按第二列进行排,如果相同的话按第七列进行排序,如果第七列也相同,那就按第五列进行排序。既然,我们取出的都是字符串,那麽,我们完全可以把第二、七、五列的字符串连接到一起,然后调用C标准库中的strcmp函数来进行比较大小,((*^__^*)嘻嘻……,知道我为什么会把上面的数据解析成字符串了吧!),现在我们只需要将连接好的新字符串进行比较,把结果返回给堆排中得建堆的过程中去,一些OK,听了我的描述,你是不是感觉这个排序过程比较简单呢?那就赶快试一试吧!