P263
排序(Sorting)是计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列。
为了便于讨论,在此首先要对排序下一个确切的定义:
假设含n个记录的序列为:{ R1, R2, ... , Rn },其相应的关键字序列为:{ K1, K2, ... , Kn },需确定1, 2, ... , n的一种排列p1, p2, ... , pn,使其相应的关键字满足如下的非递减(或非递增)关系:Kp1 <= Kp2 <= ... <= Kpn,使序列成为一个按关键字有序的序列:{ Rp1, Rp2, ... , Rpn }。这样一种操作称为排序。
上述排序定义中的关键字Ki可以是记录Ri(i = 1, 2, ... , n)的主关键字,也可以是Ri的次关键字,甚至是若干数据项的组合。考虑到待排序序列中可能存在两个或两个以上关键字相等的记录,假设Ki = Kj (1 <= i <= n, 1 <= j <= n, i != j),且在排序前的序列中Ri领先于Rj(即i < j)。若在排序后的序列中Ri仍领先于Rj,则称所用的排序方法是稳定的;反之,若可能使排序后的序列中Rj领先于Ri,则称所用的排序方法是不稳定的。
稳定排序是指,在排序过程中如果遇到两个元素相等,则它们不交换顺序。例如,1, 2, 2, 4, 3,在执行排序时第2个元素和第3个元素相等,但它们不交换顺序。不稳定排序,就是交换顺序,不管是否相同。稳定排序有:冒泡排序、插入排序、归并排序、基数排序。不稳定排序有:选择排序、快速排序、希尔排序(shell)、堆排序。
由于待排序的记录数量不同,使得排序过程中涉及的存储器不同,可将排序方法分为两大类:一类是内部排序,指的是待排序记录存放在计算机随机存储器中进行的排序过程;另一类是外部排序,指的是待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中尚需对外存进行访问的排序过程。暂时集中讨论内部排序。
内部排序的方法很多,每一种方法都有各自的优缺点,适合在不同的环境(如记录的初始排列状态等)下使用。如果按排序过程中依据的不同原则对内部排序方法时行分类,则大致可分为插入排序、交换排序、选择排序、归并排序和基数排序。如果按内部排序过程中所需的工作量来区分,则可分为3类:(1)简单的排序方法,其时间复杂度为O(n^2);(2)先进的排序方法,其时间复杂度为O(nlogn);(3)基数排序,其时间复杂度为O(d·n)。
通常在排序过程中,需要进行两种基本操作:(1)比较两个关键字的大小;(2)将记录从一个位置移动至另一个位置。前一个操作对大多数排序方法来说都是必要的,而后一个操作可以通过改变记录的存储方式来予以避免。待排序的记录序列可有下列3种存储方式:(1)存放在地址连续的一组存储单元上。它类似于线性表的顺序存储结构。在这种存储方式中,记录之间的次序关系由其存储位置决定,则实现排序必须借助移动记录;(2)一组待排序记录存放在静态链表中,记录之间的次序关系由指针指示,则实现排序不需要移动记录,仅需修改指针既可;(3)待排序记录本身存储在一组地址连续的存储单元内,同时另设一个指示各个记录存储位置的地址向量,在排序过程中不移动记录本身,而移动地址向量中这些的“地址”,在排序结束之后,再按照地址向量中的值调整记录的存储位置。假设待排序记录的数据类型设为:
#include <iostream> using std::string; #define MAXSIZE 20 typedef int KeyType; typedef struct { KeyType key; string otherinfo; }RedType; typedef struct { RedType r[MAXSIZE + 1]; int length; }SqList;
排序通常都会想到第三方的排序函数,但如果不允许用第三方的库呢?或者第三方的库太庞大了,我们
只需要一个简单的排序即可,没有那么复杂。于是这些排序的算法,在我们普通的开发过程中得到了很好的
实用机会。