若序列中i.key == j.key(即序列中两个记录的关键字相等),在排列之前i<j;若排列之后仍然有i<j,则称该排序方法是稳定的。反之,若排序之后i可能大于j,则称排序算法是不稳定的。
直接插入排序
基本思想:将一个记录插入到已经排好序的有序表中。
稳定 时间复杂度: O ( n 2 ) O(n^{2}) O(n2)
代码:
void InsertSort(int num[], int n)
{
for(int i=1; i<n; i++){
int v = num[i];
int j = i-1;
for(; j>=0 && num[j]>v; j--){
num[j+1] = num[j];
}
num[j+1] = v;
}
}
折半的插入排序代码:
void BInsertSort(int num[], int n)
{
for(int i=1; i<n; i++){
int v = num[i];
int low = 0;
int high = i-1;
while(low<=high){
int m = (low+high)>>1;
if(num[m]>v){
high = m-1;
}
else{
low = m+1;
}
}
for(int j=i-1; j>=high+1; j--){
num[j+1] = num[j];
}
num[high+1] = v;
}
}
希尔排序
基本思想:先将整个待排记录序列分割成若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
不稳定 时间复杂度和选取的增量序列有关
代码:
void ShellSort(int num[], int n)
{
for(int dk=n/2; dk>=1; dk/=2){
for(int i=dk+1; i<n; i++){
int v = num[i];
int j = i-dk;
for(; j>=0 && num[j]>v; j-=dk){
num[j+dk] = num[j];
}
num[j+dk] = v;
}
}
}
快速排序
基本思想:通过一趟排序将待排记录分割成两个独立的部分,其中一部分记录的关键字比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
不稳定 时间复杂度:平均 O ( n l o g n ) O(nlogn) O(nlogn),最坏 O ( n 2 ) O(n^{2}) O(n2)
代码:
int Partition(int num[], int low, int high)
{
int key = num[low];
while(low<high){
while(low<high && num[high]>=key){
high--;
}
num[low] = num[high];
while(low<high && num[low]<=key){
low++;
}
num[high]=num[low];
}
num[low] = key;
return low;
}
void QuickSort(int num[], int low, int high)
{
if(low<high){
int part = Partition(num, low, high);
QuickSort(num, low, part-1);
QuickSort(num, part+1, high);
}
}
选择排序
基本思想:每一趟在n-i+1个记录中选取关键字最小的记录作为有序序列中第i个记录。
不稳定 时间复杂度 O ( n 2 ) O(n^{2}) O(n2)
代码:
void SelectSort(int num[], int n)
{
for(int i=0; i<n; i++){
int pos = i;
for(int j=i+1; j<n; j++){
if(num[pos]>num[j]){
pos = j;
}
}
if(pos != i){
int temp = num[i];
num[i] = num[pos];
num[pos] = temp;
}
}
}
堆排序
基本思想:将一维数组看做二叉树,先建立一个大顶堆,将得到的最大关键字的记录和序列中第一个记录交换。然后对序列中前n-1个记录进行筛选,重新调整成大顶堆。如此反复直至排序结束。
不稳定 时间复杂性 O ( n l o g n ) O(nlogn) O(nlogn)
代码:
void HeapAdjust(int num[], int s, int m)
{
int key = num[s];
for(int j=2*s; j<m-1; j*=2){
if(j<m-1 && num[j]<num[j+1]){
j++;
}
if(key<num[j]){
num[s] = num[j];
s = j;
}
else{
break;
}
}
num[s] = key;
}
void HeapSort(int num[], int n)
{
for(int i=n/2-1; i>=0; i--){
HeapAdjust(num,i,n);
}
for(int i=n-1; i>0; i--){
int temp = num[0];
num[0] = num[i];
num[i] = temp;
HeapAdjust(num,0,i);
}
}
归并排序
基本思想:分治法。假设初始序列有n个记录,则可看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,知道得到一个长度为n的有序序列。
稳定 时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
代码:
void Merge(int num[],int temp[], int low, int mid, int high)
{
int i = low;
int j = mid+1;
int k = 0;
while(i<=mid && j<=high){
if(num[i]<=num[j]){
temp[k++] = num[i++];
}
else{
temp[k++] = num[j++];
}
}
while(i<=mid){
temp[k++] = num[i++];
}
while(j<=high){
temp[k++] = num[j++];
}
for(i=0; i<k; i++){
num[low+i] = temp[i];
}
}
void MergeSort(int num[], int temp[], int low, int high)
{
if(low<high){
int mid = (low+high)/2;
MergeSort(num,temp,low,mid);
MergeSort(num,temp,mid+1,high);
Merge(num,temp,low,mid,high);
}
}
总结
排序方法 | 平均时间 | 最坏情况 | 辅助存储 |
---|---|---|---|
简单排序 | O ( n 2 ) O(n^{2}) O(n2) | O ( n 2 ) O(n^{2}) O(n2) | O ( 1 ) O(1) O(1) |
快速排序 | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n 2 ) O(n^{2}) O(n2) | O ( l o g n ) O(logn) O(logn) |
堆排序 | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( 1 ) O(1) O(1) |
归并排序 | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n ) O(n) O(n) |