冒泡排序是一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。
void Bubble_Sort(int *pt, int N)
{
for (int i=N-1; i>0; i--){
int flag = 1;
for (int j=0; j<i; j++){
if (pt[j] > pt[j+1]){
swap(pt[j], pt[j+1]);
flag = 0;
}
}
if (flag == 1) break;
}
}
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间
void Insert_Sort(int *pt, int N)
{
for (int i=1; i<N; i++){
int temp = pt[i];
int j;
for (j=i; j>0 && temp<pt[j-1]; j--){
pt[j] = pt[j-1];
}
pt[j] = temp;
}
}
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
void Select_Sort(int *pt, int N)
{
for (int i=0;i<N;i++){
int min = pt[i], num = i;
for (int j=i+1; j<N; j++){
if (min > pt[j]){
min = pt[j];
num = j;
}
}
pt[num] = pt[i];
pt[i] = min;
}
}
逆序:具有性质 i < j 但 a[i] > a[j] 的序偶(a[i],a[j])。如序列34,8,64,51,32,21有9个逆序,即(34,8)、(34,32)、(34,31)、(64,51)、(64,32)、(64,21)、(51,32)、(51,21)以及(32,21)
注意:这正好是需要(隐含)执行的交换次数。事实总是这样,因为交换两个不按顺序排序的相邻元素恰好消除一个逆序,而一个排过序的数组没有逆序。由于算法中还有O(N)量的工作,假设 I 是原数组中的逆序数,所以插入排序的运行时间是O(I + N)。所以,如果逆序数是O(N),则插入排序以线性时间运行;冒泡排序通过加标志位也可以在有序的条件下达到最优O(N)。所以我们可以通过计算原始序列中的平均逆序数得出简单排序的平均时间精确的界。这里假设元素互异(如果允许重复,那我们连重复的平均次数都无法知道)。
定理1:N个互异数的数组的平均逆序数是N(N-1)/4。
定理2:通过交换相邻元素进行排序的任何算法平均都需要
结论:这个下界告诉我们,为了使一个排序算法以亚二次或O(N2)时间运行,必须执行一些比较,特别是要对相距较远的元素进行交换。一个排序算法通过删除逆序得以向前进行,而为了有效地进行它必须使每次交换删除不止一个逆序。
通过比较相距一定间隔的元素来工作;各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止。所以希尔排序也叫缩减增量排序。
// Origin序列
void Origin_Shell_Sort(int *pt, int N)
{
for (int P=N/2;P>0;P/=2){
for (int i=P;i<N;i++){
int temp = pt[i], j;
for (j=i;j>=P && temp<pt[j-P];j-=P){
pt[j] = pt[j-P];
}
pt[j] = temp;
}
}
}
// Sedgewick序列
void Sedgewick_Shell_Sort(int *pt, int N)
{
int Sedgewick[9] = {3905, 929, 505, 209, 109, 41, 19, 5, 1};
for (int k=0;k<9;k++){
int P = Sedgewick[k];
for (int i=P; i<N; i++){
int temp = pt[i], j;
for (j=i; j>=P && temp<pt[j-P]; j-=P){
pt[j] = pt[j-P];
}
pt[j] = temp;
}
}
}
// Hibbard序列
void Hibbard_Shell_Sort(int *pt, int N)
{
for (int k=log2(N);k>0;k--){
int P = pow(2,k)-1;
for (int i=P;i<N;i++){
int temp = pt[i], j;
for (j=i;j>=P && temp<pt[j-P];j-=P){
pt[j] = pt[j-P];
}
pt[j] = temp;
}
}
}
通过将数组元素构建大顶堆,将顶部元素取出,此时堆尾部空出一个位置,将取出元素放置堆尾部,重复进行N-1次操作
// 构建大顶堆
void PercMaxHeap(int *pt,int p, int N)
{
int parent, child, X = pt[p];
for (parent=p; parent*2+1<N; parent=child){
child = parent*2+1;
if (pt[child] < pt[child+1] && child+1 < N)
child++;
if (X > pt[child]) break;
else{
pt[parent] = pt[child];
}
}
pt[parent] = X;
}
// 堆排序
void Heap_Sort(int *pt, int N)
{
for (int p=N/2-1; p>=0; p--){
PercMaxHeap(pt, p, N);
}
for (int i=N; i>0; i--){
PercMaxHeap(pt,0,i);
int max = pt[0];
pt[0] = pt[i-1];
pt[i-1] = max;
}
}
时间复杂度:O(NlogN)
空间复杂度:O(1)
稳定性:不稳定
采用分治法:
// 递归版
void Merge(int A[], int TempA[], int L, int R, int RightEnd)
{
int T = L, LeftEnd = R-1, N = RightEnd-L+1;
while (L <= LeftEnd && R <= RightEnd){
if (A[L] <= A[R]) TempA[T++] = A[L++];
else TempA[T++] = A[R++];
}
while (L<=LeftEnd) TempA[T++] = A[L++];
while (R<=RightEnd) TempA[T++] = A[R++];
for (int i=0;i<=N;i++){
A[RightEnd] = TempA[RightEnd];
RightEnd--;
}
}
void Msort(int A[], int Temp[], int L, int RightEnd)
{
int Center = (L+RightEnd)/2;
if (L<RightEnd){
Msort(A, Temp, L, Center);
Msort(A, Temp, Center+1, RightEnd);
Merge(A, Temp, L, Center+1, RightEnd);
}
}
void Merge_Sort(int *pt, int N)
{
int *TempA = (int*)malloc(N*sizeof(int));
if (TempA!=NULL){
Msort(pt, TempA, 0, N-1);
free(TempA);
}
}
// 循环版
void MergeMinus(int A[], int TempA[], int L, int R, int RightEnd)
{
int T = L, LeftEnd = R-1;
while (L<=LeftEnd && R<=RightEnd){
if (A[L]<=A[R]) TempA[T++] = A[L++];
else TempA[T++] = A[R++];
}
while (L<=LeftEnd) TempA[T++] = A[L++];
while (R<=RightEnd) TempA[T++] = A[R++];
}
void CirMerge(int A[], int TempA[], int N, int length)
{
int i,j;
for (i=0; i<N-2*length; i+=2*length){
MergeMinus(A, TempA, i, i+length, i+2*length-1);
}
if (i+length < N)
MergeMinus(A, TempA, i, i+length, N-1);
else
for (j=i; j<N; j++) TempA[j] = A[j];
}
void CirMerge_Sort(int *pt, int N)
{
int *TempA = (int*)malloc(N*sizeof(int));
int length = 1;
if (TempA!=NULL){
while (length<N){
CirMerge(pt, TempA, N, length);
length *= 2;
CirMerge(TempA, pt, N, length);
length *= 2;
}
free(TempA);
}
}
时间复杂度:O(NlogN)
空间复杂度:O(N)
稳定性:稳定
归并排序经常应用于外部大数据排序,具体操作可以参考这篇文章 外部排序
void Swap(int *p1, int *p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int Median3(int pt[], int left, int right)
{
int center = (left+right)/2;
if (pt[left]>pt[center])
Swap(&pt[left], &pt[center]);
if (pt[left]>pt[right])
Swap(&pt[left], &pt[right]);
if (pt[center]>pt[right])
Swap(&pt[center], &pt[right]);
Swap(&pt[center], &pt[right-1]);
return pt[right-1];
}
// 插入排序
void QInsert_Sort(int *pt, int Left, int Right)
{
for (int i=Left;i<=Right;i++){
int temp = pt[i], j;
for (j=i;j>0 && temp<pt[j-1];j--){
pt[j] = pt[j-1];
}
pt[j] = temp;
}
}
void Qsort(int A[], int Left, int Right)
{
int CutOff = 100; // 当元素数目小于100时,使用插入排序
if (CutOff<=Right-Left){
int Pivot = Median3(A, Left, Right);
int Low = Left, High = Right-1;
while (1){
while (A[++Low]<Pivot);
while (A[--High]>Pivot);
if (Low<High)
Swap(&A[Low], &A[High]);
else
break;
}
Swap(&A[Low], &A[Right-1]);
Qsort(A, Left, Low-1);
Qsort(A,Low+1, Right);
}
else
QInsert_Sort(A, Left, Right);
}
void Quick_Sort(int *pt, int N)
{
Qsort(pt, 0, N-1);
}