1:冒泡算法
1.1 实现
int i;
int j;
int flag;
for(i = 0; i < n; i++)
{
flag = 0;
for(j = 0; j < n - i - 1; j++)
if(a[j] > a[j + 1])
{
swap(a[j], a[j + 1]);
flag = 1;
}
if(flag == 0)
break;
}
1.2 平均复杂度
n轮循环,每一轮循平均的复杂度为n/2,故总的复杂度为:O(n * n)
1.3 最好情况
当输入序列已经排序好的时候,一轮冒泡下来没有交换过位置,直接退出程序,复杂度为:O(n)
1.4 最坏情况
当输入序列是你逆序的时候,每一次比较都要进行交换,复杂度为:O(n*n)
1.5 稳定性
a = b的时候,由于只有大于才做交换,故a,b的位置没有机会交换,所以,冒泡排序是稳定的
2.1 实现
int tmp;
int i, j, k;
for(i = 0; i < n; ++i)
{
tmp = a[i];
for(j = 0; j < i; ++j)
{
if(tmp < a[j])
{
for(k = i; k > j; --k)
{
a[k] = a[k - 1];
}
a[j] = tmp;
break;
}
}
}
int tmp;
int i, j;
for(i = 1; i < n; ++i)
{
for(j = i, tmp = a[i]; a[j - 1] > tmp && j > 0; --j)
{
a[j] = a[j - 1];
}
a[j] = tmp;
}
2.2 平均复杂度
插入的次数为n,而每次插入操作需要移动的数据平均复杂度为O(n),故总的平均复杂度为:O(n*n)
2.3 最好情况
当输入的序列已经有序的时候,只需要进行比较,不用移动数据,故其复杂度仅仅为O(n)
2.4 最坏情况
当输入的序列为逆序的时候,每次插入都要移动之前所有元素,故其复杂度为O(n*n)
2.5 稳定性
如果a=b,则当a在前面时候,其必然比b先确定,而后面b再插入时,必然在a后面,故该算法稳定
3.1 实现
int tmp;
int gap, i, j;
for(gap = n / 2; gap > 0; gap /= 2)
{
for(i = gap; i < n; ++i)
{
tmp = a[i];
j = i;
for(; j >= gap && tmp < a[j - gap]; j -= gap)
a[j] = a[j - gap];
a[j] = tmp;
}
}
3.2 平均复杂度
由于使用的增量(程序中的gap)不同,导致的复杂度也不同,常用的有Hibbard增量,基于模拟的结果被认为是O(n^(5/4))
3.3 最好情况
当输入序列已经有序的时候,每一次增量向前比较都立即不满足条件而退出,故只有前面两层循环起到了作用,复杂度为O(nlogn)
3.4 最坏情况
使用Hibbard增量的时候,该排序算法的最坏情况复杂度为O(n^(3/2))(已经被证明)
3.5 稳定性
由于不同的gap间隔对应的数据是独自比较的,所以,如果a=b但是不在同一个gap间隔上,显然就会出现前后颠倒的情况,即是说,该算法不稳定
4.1 实现
int i, j;
// build heap
for(i = n / 2; i >= 0; i--)
percDown(a, i, n);
// delete Max
for(j = n - 1; j > 0; j--)
{
swap(a[0], a[j]);
percDown(a, 0, j);
}
return 2 * i;
int child;
int tmp;
for(tmp = a[i]; leftChild(i) < n; i = child)
{
child = leftChild(i);
if(child != n - 1 && a[child] < a[child + 1])
child++;
if(tmp < a[child])
a[i] = a[child];
else
break;
}
a[i] = tmp;
4.2 平均复杂度
排序的过程主要取决于后期的调整(前期的建立堆的过程只需要O(logn * logn)),其复杂度为O(nlongn)
4.3 最好情况
对于堆排序,无论输入的序列是否有序或者其他的条件,建立和调整堆的复杂度都不会受到影响,故最好情况的复杂度为O(nlogn)
4.4 最坏情况
对于堆排序,无论输入的序列是否有序或者其他的条件,建立和调整堆的复杂度都不会受到影响,故最坏情况的复杂度为O(nlogn)
4.5 稳定性
不稳定,暂时没有想到好的说明例子,但是如果画出其二叉树的表示图,就可以直观地看出,a=b时,如果b后于a的插入,其将在a的后面,但是经过percDown调整后,b有可能被调到比a更高的层次(也就是a的前面了),所以,顺序可能存在调换情况。
5.1 实现
int *b = new int[n];
mergeSort(a, b, 0, n - 1);
delete b;
if(left < right)
{
int center = (left + right) / 2;
mergeSort(a, tmp, left, center);
mergeSort(a, tmp, center + 1, right);
merge(a, tmp, left, center + 1, right);
}
int leftEnd = rightPos - 1;
int tmpPos = leftPos;
int numElements = rightEnd - leftPos + 1;
while(leftPos <= leftEnd && rightPos <= rightEnd)
{
if(a[leftPos] < a[rightPos])
tmp[tmpPos++] = a[leftPos++];
else
tmp[tmpPos++] = a[rightPos++];
}
while(leftPos <= leftEnd)
tmp[tmpPos++] = a[leftPos++];
while(rightPos <= rightEnd)
tmp[tmpPos++] = a[rightPos++];
for(int i = 0; i < numElements; i++, rightEnd--)
a[rightEnd] = tmp[rightEnd];
5.2 平均复杂度
对于各种输入序列,归并排序的处理过程都是一样的,T(1) = 1, T(N) = T(N/2) + N,不难求得最后的复杂度T(N)为O(NlogN)
5.3 最好情况
与平均情况一致
5.4 最坏情况
与平均情况一致
5.5 稳定性
由于没有发生数据交换,所有当a=b的时候,a一开始如果在b前面,则其每一次合并后仍然在b前面,故该排序算法是稳定的
6.1 实现
quickSort(a, 0, n - 1);
if(left + 10 > right)
insertSort(a, right - left + 1);
else
{
int pivot = median3(a, left, right);
int i = left, j = right;
for(;;)
{
while(a[++i] < pivot);
while(a[--j] > pivot);
if(i < j)
swap(a[i], a[j]);
else
break;
}
// reset pivot
swap(a[i], a[right - 1]);
quickSort(a, left, i - 1);
quickSort(a, i + 1, right);
}
int center = (left + right) / 2;
if(a[center] < a[left])
swap(a[left], a[center]);
if(a[right] > a[left])
swap(a[left], a[right]);
if(a[right] < a[center])
swap(a[center], a[right]);
// place pivot at position right - 1
swap(a[center], a[right - 1]);
return a[right - 1];
6.2 平均复杂度
平均复杂度为O(NlogN)
6.3 最好情况
每次都刚好选到了一个区间段的中位数,相当于归并排序的过程,复杂度为O(NlongN)
6.4 最坏情况
每次都刚好选到了最小的元素作为主元,导致极度的不平衡,退化为插入排序的最坏情况了,复杂度为O(N^2)
6.5 稳定性
当a=b>pivot且a在b前面的时候,由于从后面开始遍历,故b会先于a被替换到pivot的前面,这样,b就变成了在a的前面,也就是说,ab位置对调,故该排序算法不稳定
7.1 实现
int i, j;
int tmp;
for(i = 0; i < n; ++i)
{
tmp = i;
for(j = i + 1; j < n; j++)
if(a[j] < a[tmp])
tmp = j;
swap(a[i], a[tmp]);
}
7.2 平均复杂度
每次循环需要访问n-i个元素得到最小值,故其总的比较次数为(N+1)*N/2,故复杂度为O(N^2)
7.3 最好情况
与平均情况一致
7.4 最坏情况
与平均情况一致
7.5 稳定性
假设a=b,并且a在b的前面,而在某轮循环中最小值在b的后面,而次最小值需要跟a交换,那么该轮过后,b就在a前面了,所以,该排序算法不稳定
void bubbleSort(int *a, int n)
{
}
2:插入排序
void insertSort(int *a, int n)
// 该算法是最初始的实现,顺序比较前面元素,效率较低
{
}
void simpleInsertSort(int *a, int n)
PS: 以下代码引自《数据结构与算法分析:C++描述》(第3版)
// 该算法插入时,逆序比较前面一排序元素,效率较高
{
}
3:shell排序
PS: 以下代码引自《数据结构与算法分析:C++描述》(第3版)
void shellSort(int *a, int n)
// 该程序使用的增量为shell增量,最坏情况为O(n*n)
// 更好的效果可以选择Hibbard增量:2^k - 1
{
}
4:堆排序
PS: 以下代码引自《数据结构与算法分析:C++描述》(第3版)
void percDown(int *, int , int );
void heapSort(int *a, int n)
{
}
inline int leftChild(int i)
{
}
void percDown(int *a, int i, int n)
{
}
5:归并排序
PS: 以下代码引自《数据结构与算法分析:C++描述》(第3版)
void merge(int *, int *, int , int , int );
void mergeSort(int *, int *, int , int );
void simpleMergeSort(int *a, int n)
{
}
void mergeSort(int *a, int *tmp, int left, int right)
{
}
void merge(int *a, int *tmp, int leftPos, int rightPos, int rightEnd)
{
}
6:快速排序
PS: 以下代码引自《数据结构与算法分析:C++描述》(第3版)
int median3(int *, int , int);
void quickSort(int *, int , int);
void simpleQuickSort(int *a, int n)
{
}
void quickSort(int *a, int left, int right)
{
// if number of elements less than 10, used insertSort
}
// find out median of three elements and set pivot
int median3(int *a, int left, int right)
{
}
7:直接选择排序
void selectSort(int *a, int n)
{
}