简单排序方法

最近没什么事情,在网上找了一点关于排序的资料。

它是怎么解释的:

   排序(Sort)是计算机程序设计中的一种重要操作,也是日常生活中经常遇到的问题。例如,字典中的单词是以字母的顺序排列,否则,使用起来非常困难。同样,存储在计算机中的数据的次序,对于处理这些数据的算法的速度和简便性而言,也具有非常深远的意义。
   基本概念
排序是把一个记录(在排序中把数据元素称为记录)集合或序列重新排列成按记录的某个数据项值递增(或递减)的序列。
表7-1是一个学生成绩表,其中某个学生记录包括学号、姓名及计算机文化基础、C语言、数据结构等课程的成绩和总成绩等数据项。在排序时,如果用总成绩来排序,则会得到一个有序序列;如果以数据结构成绩进行排序,则会得到另一个有序序列。


表1 学生成绩表
学号            姓名    计算机文化基础 C语言 数据结构 总成绩
04103101   张三      85                 92      86       263
04103102   李四      90                 91      93       274
04103103   王五      66                 63      64       193
04103104   何六      75                 74      73       222
…               …          …                  …        …        …


作为排序依据的数据项称为“排序项”,也称为记录的关键码(Keyword)。关键码分为主关键码(Primary Keyword)和次关键码(Secondary Keyword)。一般地,若关键码是主关键码,则对于任意待排序的序列,经排序后得到的结果是唯一的;若关键码是次关键码,排序的结果不一定唯一,这是因为待排序的序列中可能存在具有相同关键码值的记录。此时,这些记录在排序结果中,它们之间的位置关系与排序前不一定保持一致。如果使用某个排序方法对任意的记录序列按关键码进行排序,相同关键码值的记录之间的位置关系与排序前一致,则称此排序方法是稳定的;如果不一致,则称此排序方法是不稳定的。


例如,一个记录的关键码序列为(31,2,15,7,91,7*),可以看出,关键码为7的记录有两个(第二个加“*”号以区别,以下同)。若采用一种排序方法得到的结果序列为(2,7,7*,15,31,91),则该排序方法是稳定的;若采用另外一种排序方法得到的结果序列为(1,7*,7,15,31,91),则这种排序方法是不稳定的。
由于待排序的记录的数量不同,使得排序过程中涉及的存储器不同,可将排序方法分为内部排序(Internal Sorting)和外部排序(External Sorting)两大类。
内部排序指的是在排序的整个过程中,记录全部存放在计算机的内存中,并且在内存中调整记录之间的相对位置,在此期间没有进行内、外存的数据交换。
外部排序指的是在排序过程中,记录的主要部分存放在外存中,借助于内存逐步调整记录之间的相对位置。在这个过程中,需要不断地在内、外存之间交换数据。
显然,内部排序适用于记录不多的文件。而对于一些较大的文件,由于内存容量的限制,不能一次全部装入内存进行排序,此时采用外部排序较为合适。但外部排序的速度比内部排序要慢的多。内部排序和外部排序各有许多不同的排序方法。


排序问题的记录采用线性结构,同时,允许存取任意位置的记录,这和线性表完全吻合。所以,排序问题的数据结构是线性表。
任何算法的实现都和算法所处理的数据元素的存储结构有关。线性表的两种典型存储结构是顺序表和链表。由于顺序表具有随机存取的特性,存取任意一个数据元素的时间复杂度为O(1),而链表不具有随机存取特性,存取任意一个数据元素的时间复杂度为O(n),所以,排序算法基本上是基于顺序表而设计的。


由于排序是以记录的某个数据项为关键码进行排序的,所以,为了讨论问题的方便,假设顺序表中只存放记录的关键码,并且关键码的数据类型是整型,也就是说,这里使用的顺序表是整型的顺序表。

  由于只看了简单排序,简单排序方法有分为(直接插入排序)(冒泡排序)(简单选择排序)。

直接插入排序(direct Insert Sort)的基本思想:

  顺序地将待排序的记录按其关键码的大小插入到已排序的记录子序列的适当位置。子序列的记录个数从1开始逐渐增大,当子序列的记录个数与顺序表中的记录个数相同时排序完毕。设待排序的顺序表sequenceTable中有n个记录,初始时子序列中只有一个记录sequenceTable[0]。第一次排序时,准备把记录sequenceTable[1]插入到已排好序的子序列中,这时只需要比较sequenceTable[0]和sequenceTable[1]的大小,若sequenceTable[0]≤sequenceTable[1]说明序列已有序,否则将sequenceTable[1]插入到sequenceTable[0]的前面,这样子序列的大小增大为2。第二次排序时,准备把记录sequenceTable[2]插入到已排好序的子序列中,这需要先比较sequenceTable[2] 和sequenceTable[1]以确定是否需要把sequenceTable[2]插入到sequenceTable[1]之前。如果sequenceTable[2]插入到sequenceTable[1]之前,再比较sequenceTable[2]和sequenceTable[0]以确定是否需要把sequenceTable[2]插入到sqList[0]之前。这样的过程一直进行到sequenceTable[n-1]插入到子序列中为止。这时,顺序表sequenceTable就是有序的。
直接插入排序的算法如下所示,算法中记录的比较表示记录关键码的比较,顺序表中只存放了记录的关键码:

View Code
1 public void InsertSort(Int32[] sort)
2 {
3 for (var i = 1; i < sort.Length; ++i)
4 {
5 if (sort[i] < sort[i-1])
6 {
7 Int32 temp = sort[i];
8 Int32 j = 0;
9 for (j = i-1;j>=0&&temp<sort[j]; --j)
10 {
11 sort[j + 1] = sort[j];
12 }
13 sort[j+1] = temp;
14 }
15 }
16 }

直接插入排序算法的时间复杂度分为最好、最坏和随机三种情况:
(1) 最好的情况是顺序表中的记录已全部排好序。这时外层循环的次数为n-1,内层循环的次数为0。这样,外层循环中每次记录的比较次数为1,所以直接插入排序算法在最好情况下的时间复杂度为O(n)。
(2) 最坏情况是顺序表中记录是反序的。这时内层循环的循环系数每次均为i。这样,直接插入排序算法在最坏情况下的时间复杂度为O(n2)。
(3) 如果顺序表中的记录的排列是随机的,则记录的期望比较次数为n2/4。因此,直接插入排序算法在一般情况下的时间复杂度为O(n2)。
可以证明,顺序表中的记录越接近于有序,直接插入排序算法的时间效率越高,其时间效率在O(n)到O(n2)之间。
直接插入排序算法的空间复杂度为O(1)。因此,直接插入排序算法是一种稳定的排序算法。

  冒泡排序(Bubble Sort)的基本思想是:将相邻的记录的关键码进行比较,若前面记录的关键码大于后面记录的关键码,则将它们交换,否则不交换。
设待排序的顺序表sequenceTable中有n个记录,冒泡排序要进行n-1趟,每趟循环均是从最后两个记录开始。第1趟循环到第2个记录的关键码与第1个记录的关键码比较后终止,第2趟循环到第3个记录的关键码与第2个记录的关键码比较结束后终止。一般地,第i趟循环到第i+1个记录的关键码与第i个记录的关键码比较后终止,所以,第n-1趟循环到第n个记录的关键码与第n-1个记录的关键码比较后终止。
冒泡排序算法的实现如下所示,算法中记录的比较表示记录关键码的比较,顺序表中只存放了记录的关键码:

View Code
1 public void BubbleSort(Int32[] value)
2 {
3 Int32 temp;
4 for (var i = 0; i < value.Length-1; i++)
5 {
6 for (var j = value.Length-2; j >= i; j--)
7 {
8 if (value[j+1] < value[j])
9 {
10 temp = value[j + 1];
11 value[j + 1] = value[j];
12 value[j] = temp;
13 }
14 }
15 }
16 }

冒泡排序算法的最好情况是记录已全部排好序,这时,循环n-1次,每次循环都因没有数据交换而退出。因此,冒泡排序算法在最好情况下的时间复杂度为O(n)。冒泡排序算法的最坏情况是记录全部逆序存放,这时,循环n-1次,总比较次数为:总的移动次数为比较次数的3倍,因为被进行一次比较,需要进行3次移动。因此,冒泡排序算法在最坏情况下的时间复杂度为O(n2)。
冒泡排序算法只需要一个辅助空间用于交换记录,所以,冒泡排序算法是一种稳定的排序方法。

  简单选择排序(Simple Select Sort)算法的基本思想是:从待排序的记录序列中选择关键码最小(或最大)的记录并将它与序列中的第一个记录交换位置;然后从不包括第一个位置上的记录序列中选择关键码最小(或最大)的记录并将它与序列中的第二个记录交换位置;如此重复,直到序列中只剩下一个记录为止。
设待排序的顺序表sequenceTable中有n个记录,简单选择排序要进行n-1趟,第1趟从n个记录选择关键码最小(或最大)的记录并与第1个记录交换位置;第2趟从第2个记录开始的n-1个记录中选择关键码最小(或最大)的记录并与第2个记录交换位置。一般地,第i趟从第i个记录开始的n-i+1个记录中选择关键码最小(或最大)的记录并与第i个记录交换位置,所以,第n-1趟比较最后两个记录选择关键码最小(或最大)的记录并与第n-1个记录交换位置。
简单选择排序算法的实现如下所示,算法中记录的比较表示记录关键码的比较,顺序表中只存放了记录的关键码:

View Code
1 public void SimpleSelectSort(Int32[] value)
2 {
3 Int32 temp=0;
4 Int32 t = 0;
5 for (var i = 0; i < value.Length-1; i++)
6 {
7 t = i;
8 for (var j = i + 1; j <= value.Length-1; j++)
9 {
10 if (value[t] > value[j])
11 {
12 t = j;
13 }
14 }
15 temp = value[i];
16 value[i] = value[t];
17 value[t] = temp;
18 }
19 }

在简单选择排序中,第一次排序要进行n-1次比较,第二次排序要进行n-2次比较,第n-1排序要进行1次比较,在各次排序时,记录的移动次数最好0次,最坏为3次,所以,总的移动次数最好为0次,最坏为3次。因此,简单选择排序算法的时间复杂度为O(n2)。
简单选择排序算法只需要一个辅助空间用于交换记录,所以,简单选择排序算法是一种稳定的排序方法。

  

posted @ 2011-03-17 13:10  @H  阅读(506)  评论(0编辑  收藏  举报