排序(sorting):将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列。排序分内部排序和外部排序。
外部排序:指的是待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过尚需对外存进行访问的排序过程。
内部排序:指的是待排序记录存放在计算机随机存储器中进行的排序过程。
依据不同原则对内部排序方法进行分类:交换排序、插入排序、选择排序、归并排序、基数排序。通常,在排序过程中需进行两种基本操作:(1)比较两个关键字的大小;(2)将记录从一个位置移动另一个位置。前一个操作对大多数排序方法都是不要的,而后一个操作可以通过改变记录的存储方式以避免;3种存储方式:1)待排序的一组记录存放在地址连续的一组存储单元上;必须借助移动记录来实现。2)一组待排序记录存放在静态链表中;不需要移动记录,仅需修改指针即可(又称表排序)。3)待排序记录本身存储在一组连续的存储单元内,同时另设一个指示各个记录的存储位置的地址向量;不需要移动记录本身,需移动地址向量中这些记录的地址(又称地址排序)。
注:排序稳定性:待排序记录记录中,存在两个或两个以上的记录具有相同的关键字,在用某种排序法排序后,若这些相同关键字的元素的相对次序仍然不变,则这种排序方法是稳定的;反之这种排序方法是不稳定的。(1)交换排序:根据序列中两个键值的比较结果来对换两个记录在序列中的位置;特点:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。冒泡排序(bubble sort):第i趟冒泡排序是从a[0]到a[n-i]依次比较相邻两个记录的关键字,并在逆序时交换相邻记录(可以用一趟排序过程中没有进行交换记录的操作作为结束条件)。快速排序(quick sort):是对冒泡排序的一种改进。通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。一趟快速排序的步骤:附设两个指针low和high,设枢轴记录的关键字为pivotKey;则首先从high所指位置起向前搜索找到第一个关键字小于pivotKey的记录和枢轴记录交换;然后从low所指位置起向后搜索找到第一个关键字大于pivotKey的记录和枢轴记录交换;重复这两步直至low=high(在排序过程中对枢轴位置的赋值是多余的,因为只有在一趟排序结束时即low=high的位置才是枢轴的最后位置)。(2)插入排序(insert sort):将有序记录从后向前扫描找到合适的位置插入未排序记录,对未排序记录从前向后扫描直至所有的记录都被排序。直接插入排序(straight insert sort):将一个记录插入到已经排好的有序表中,从而得到一个新的、记录数增加1的有序表。第i(i>=2)趟直接插入排序:在含有i-1个记录有序子序列中a[i-2]中插入一个记录a[i-1];后变为含有i个记录的有序子序列a[i-1];在自i-2起向前搜索过程中,可以同时向后移动记录;整个排序过程进行n-1趟插入。希尔排序(shell’s sort):又称缩小增量排序,是对直接插入排序的一种改进;先将整个待排序记录分割成为若干子序列分别进行直接插入排序(一趟希尔排序),待整个序列中的记录基本有序时,再对全体记录进行一次直接插入排序。特点:子序列的构成不是简单的“逐段分割”而是将相隔某个“增量”的记录组成一个子序列。(3)选择排序(selection sort):每一趟在n-i+1(i=1,2, … ,n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录。简单选择排序(simple selection sort):第i趟简单选择排序,通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i个记录交换之。堆排序(heap sort):n个元素的序列{k1,k2, … ,kn}当且仅当满足下列关系称为堆。Ki<=k2i且ki<=k2i+1;或Ki>=k2i且ki>=k2i+1(i=1,2,… ,[n/2]);若在输出堆顶的最小值后,使得剩余n-1个元素的序列重建成一个堆,则得到n个元素的次小值,反复执行便能得到一个有序序列,这个过程称之为堆排序。(4)归并排序(merging sort):初始数列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2]个长度为2或1的有序子序列;再两两归并,… ,如此重复直至得到一个长度为n的有序序列为止,这种排序方法称为2-路归并排序。整个归并操作需要进行[log2n]趟。(5)基数排序(radix sort):不需要进行记录关键字间的比较。是一种借助多关键字排序的思想对单逻辑关键字进行排序的方法。
排序方法 | 时间复杂度 | 空间复杂度 | 稳定性 | |
交换排序 | 冒泡排序 | O(n^2) | O(1) | 稳定 |
快速排序 | O(nlogn) | O(nlogn) | 不稳定 | |
选择排序 | 简单选择排序 | O(n^2) | O(1) | 不稳定 |
堆排序 | O(nlogn) | O(1) | 不稳定 | |
归并排序 | O(nlogn) | O(n) | 稳定 | |
基数排序 | O(d*n) | O(d*n) | 稳定 |
//bubbleSort
void bubbleSort(int a[], int n){
int temp;
for (int i = 0; i < n - 1;i++){
//第i趟冒泡排序是从a[0]到a[n-i]依次比较相邻两个记录的关键字
for (int j = 0; j < n - i - 1; j++){
if (a[j] > a[j + 1]){
temp = a[j];
a[j] = a[j+1];
a[j + 1] = temp;
}
}
}
}
//partition:一趟快速排序
int partition(int a[],int low,int high){
int pivotKey = a[low]; //枢轴记录的关键字
while (low<high){
while (low < high&&a[high] >= pivotKey)
high--;
a[low] = a[high]; //将比枢轴小的交换到低端
while (low < high&&a[low] <= pivotKey)
low++;
a[high] = a[low]; //将比枢轴大的交换到高端
}
a[low] = pivotKey; //一趟排序结束,将枢轴记录到相应的位置low
return low; //返回枢轴所在的位置
}
//quickSort:快速排序的递归实现
void quickSort(int a[],int low,int high){
if (low<high){ //长度大于1
int pivotLoc = partition(a,low,high); //将data表一分为二,pivotLoc是枢轴位置
quickSort(a, low, pivotLoc - 1); //对低子表递归排序
quickSort(a, pivotLoc+1, high); //对高子表递归排序
}
}
//straight insertion sort:直接插入排序
void insertSort(int a[], int n){
int x; //哨兵
for (int i = 1; i < n; i++){ //n-1趟插入
if (a[i] < a[i - 1]){
x = a[i]; //复制为哨兵
int j; //待插入的位置
for (j = i - 1; x < a[j]; j--) //确定带插入位置j,并将记录后移
a[j + 1] = a[j];
a[j + 1] = x; //插入到正确位置
}
}
}
//shell sort:为一趟希尔排序;重复多次直至dk为1希尔排序结束
void shellSort(int a[],int n,int dk){
//dk是前后记录的增量,而不是1
int x;
for (int i = dk; i < n;i++){
if (a[dk] < a[i - dk]){
x = a[i];
int j;
for (j = i - dk; j >= 0 && x < a[j]; j -= dk)
a[j + dk] = a[j];
a[j + dk] = x;
}
}
}
//simple selection sort:简单选择排序
void selectSort(int a[],int n){
int index; //每一趟选择排序的最小值的索引
int min; //每一趟选择排序的最小值
for (int i = 0; i < n - 1; i++){
index = i;
for (int j = i + 1; j < n; j++)
if (a[index] > a[j])
index = j;
min = a[index];
a[index] = a[i];
a[i] = min;
}
}
//merging sort:归并排序;一趟归并操作需要调用[n/2h]次算法merge,h为子序列长度
void merge(int a[],int b[],int i,int m,int n){
//将有序的a[i, ... ,m]和a[m+1, ... ,n]归并为有序的b[i, ... ,n]
int j; //有序的a[m+1, ... ,n]的索引
int k; //b[i, ... ,n]的索引
for (j = m + 1, k = i; i <= m&&j <= n;k++){ //将a中的记录由小到大并入b中
if (a[i] < a[j])
b[k] = a[i++];
else
b[k] = a[j++];
}
while (i <= m) //将剩余的a[i, ... ,m]复制到b中
b[k++] = a[i++];
while (j <= n) //将剩余的a[j, ... ,n]复制到b中
b[k++] = a[j++];
}
void mergeSort(int a[],int b[],int s,int t){
//将a[s, ... ,t]归并排序为b[s, ... ,t]
int* b1;
if (s == t)
b[s] = a[s];
else{
int m = (s + t) / 2;
mergeSort(a,b1,s,m);
mergeSort(a,b1,m+1,t);
merge(b1,b,s,m,t);
}
}