几种排序算法

本帖依据学习进度持续更新

《数据结构与算法分析-c语言描述》学到第七章,是时候该系统的学习一下排序算法了。首先学到的是插入排序,算法就不赘述了,书上博客上到处都有。书上的两个定理还不太明白:

插入排序

定理7.1

N个互异数的数组的平均逆序数是N(N-1)/4。

定理7.2

通过交换相邻元素进行排序的任何算法平均需要Ω(N^2)时间。

插入排序的算法复杂度应该为O(N^2)才对。

下面是我的测试用例:

void insertionSort(elementType A[], int N)
{
	Int j, P;
	elementType Tmp;
	for(P = 1; P < N; P++)
	{
		Tmp = A[P];
		for(j = P; j > 0 && A[j – 1] > Tmp; j--)
			A[j] = A[j – 1];
		A[j] = Tmp;
	}
}

希尔排序

希尔排序的最后一样增量是1,所以是直接插入排序,但移动次数比直接插入排序少,代入数组测试:

#include<stdio.h>
#define elementType int
void shellSort(elementType A[], int N)
{
    int i, j, Increment;
    elementType Tmp;
   
    for(Increment = N/2; Increment > 0; Increment /= 2)
        for(i = Increment; i < N; i++)
        {
            Tmp = A[i];
            for(j = i; j >= Increment && Tmp < A[j-Increment]; j -= Increment)
                A[j] = A[j-Increment];
            A[j] = Tmp;
        }
}
void main()
{
    int i;
    int a[] = {81,94,11,96,12,35,17,95,28,58,41,75,15};
    shellSort(a, 13);
    for(i = 0; i < 13; i++)
        printf("%d ", a[i]);
}

堆排序

我们为了找出一列数第K个最小的数引入二叉堆,最小堆总能从根节点输出最小值,一直使用deleteMin函数就能输出一列排序好的数,因为最小值最先输出,所以这个数列是递减的。由此我们可以得出如果建一个最大堆,使用deleteMax函数就能输出一列递增的序列。时间复杂度为O(NlogN)。

#include<stdio.h>
#define leftChild(i) (2*i+1)
void Swap(int *a, int *b)
{
	int Tmp = *a;
	*a = *b;
	*b = Tmp;
}
void percDown(int A[], int i, int N)
{
	int Child, Tmp;
	
	for(Tmp = A[i]; leftChild(i) < N; i = Child)
	{
		Child = leftChild(i);
		if(Child != N-1 && A[Child + 1] > A[Child])
			Child++;
		if(A[Child] > Tmp)
			A[i] = A[Child];
		else
			break;
	}
	A[i] = Tmp;
}
void heapSort(int A[], int N)
{
	int i;
	for(i = N / 2; i >= 0; i--)
		percDown(A, i, N);
	for(i = N - 1; i > 0; i--)
	{
		Swap(&A[0], &A[i]);
		percDown(A, 0, i);
	}
}
void main()
{
	int i;
	int a[] = {81,94,11,96,12,35,17,95,28,58,41,75,15}; 
	heapSort(a, 13);
	for(i = 0; i < 13; i++)
		printf("%d ", a[i]);
}


归并排序

该算法是经典的分治策略,基本的操作是合并两个已排序的表,放到第三个表中。而已排序的表又可以通过归并排序本身递归得到。

#include<stdio.h>
#include<stdlib.h>
#define elementType int
void Merge(elementType A[], elementType Tmp[], int Lpos, int Rpos, int rightEnd)
{
    int i, leftEnd, numElements, Tmppos;
    leftEnd = Rpos - 1;
    Tmppos = Lpos;
    numElements = rightEnd - Lpos + 1;

    while(Lpos <= leftEnd && Rpos <= rightEnd)
        if(A[Lpos] <= A[Rpos])
            Tmp[Tmppos++] = A[Lpos++];
        else
            Tmp[Tmppos++] = A[Rpos++];

    while(Lpos <= leftEnd)
        Tmp[Tmppos++] = A[Lpos++];
    while(Rpos <= rightEnd)
        Tmp[Tmppos++] = A[Rpos++];

    for(i = 0; i < numElements; i++, rightEnd--)
        A[rightEnd] = Tmp[rightEnd];
}
void Msort(elementType A[], elementType Tmp[], int Left, int Right)
{
    int Center;

    if(Left < Right)
    {
        Center = (Left + Right) / 2;
        Msort(A, Tmp, Left, Center);
        Msort(A, Tmp, Center + 1, Right);
        Merge(A, Tmp, Left, Center + 1, Right);
    }
}
void mergeSort(elementType A[], int N)
{
    elementType *Tmp;

    Tmp = malloc(N * sizeof(elementType));
    if(Tmp != NULL)
    {
        Msort(A, Tmp, 0, N - 1);
        free(Tmp);
    }
    else
        printf("No space for Tmp array");
}
void main()
{
    int i;
    int a[] = {81,94,11,96,12,35,17,95,28,58,41,75,15};
    mergeSort(a, 13);
    for(i = 0; i < 13; i++)
        printf("%d ", a[i]);
}
    



快速排序

书上的一种以三数中值作为基准值的快速排序,当数组长度小于3的时候选用插入排序,用的递归实现。

#include<stdio.h>
#define Cutoff 3
void Swap(int *a, int *b)
{
    int Tmp = *a;
    *a = *b;
    *b = Tmp;
}
void insertionSort(int A[], int N)
{
     int tmp;
     int j, p;
 
    for( p = 1; p < N; p++ )
     {
         tmp = A[p];
         for( j = p; j > 0 && A[j -1] > tmp; j -- )
             A[j] = A[j-1];
         A[j] = tmp;
     }
 }
int Median3(int A[], int Left, int Right)
{
	int Center = (Left + Right) / 2;
 
	if(A[Left] > A[Center])
		Swap(&A[Left], &A[Center]);
	if(A[Left] > A[Right])
		Swap(&A[Left], &A[Right]);
	if(A[Center] > A[Right])
		Swap(&A[Center], &A[Right]);
 
	Swap(&A[Center], &A[Right - 1]);
	return A[Right - 1];
}
void Qsort(int A[], int Left, int Right)
{
    int i, j, Pivot;
 
	if(Left + Cutoff <= Right)
    {
		i = Left;
		j = Right - 1;
		Pivot = Median3(A, Left, Right);
		for(; ; )
		{
			while(A[++i] < Pivot){}
			while(A[--j] > Pivot){}	
			if(i < j)
				Swap(&A[i], &A[j]);
			else
				break;
		}
		Swap(&A[i], &A[Right - 1]);
 
		Qsort(A, Left, i - 1);
		Qsort(A, i + 1, Right);
	}
	else
		insertionSort(A + Left, Right - Left + 1);
}
void quickSort(int A[], int N)
{
    Qsort(A, 0, N - 1);
}
void main()  
{  
    int i;  
    int a[] = {81,94,11,96,12,35,17,95,28,58,41,75,15};   
    quickSort(a, 13);  
    for(i = 0; i < 13; i++)  
        printf("%d ", a[i]);  
}

下面是自己写的以第一个数作为基准值的快速排序。实际上这中基准值的选取是非常糟糕的,如果输入是预排序的或是反序的,第一次分割就总是分割到一边,时间复杂度为O(N^2),仅作为编程练习。

#include<stdio.h>
#define Cutoff 3
void Swap(int *a, int *b)
{
    int Tmp = *a;
    *a = *b;
    *b = Tmp;
}
void Qsort(int A[], int Left, int Right)
{
    int i, j, Pivot;
 
<span style="white-space:pre">	</span>if(Left < Right)
    {
		Pivot = A[Left];
		i = Left;
		j = Right - 1;
		Swap(&A[Left], &A[Right]);

		while(i <= j)
		{
			while(A[i] < Pivot)
				i++;
			while(A[j] > Pivot)
				j--;
			if(i < j)
			{
				Swap(&A[i], &A[j]);
				i++;
				j--;
			}
			else
				break;
		}
		Swap(&A[i], &A[Right]);

		Qsort(A, Left, i - 1);
		Qsort(A, i + 1, Right);
	}
}
void quickSort(int A[], int N)
{
    Qsort(A, 0, N - 1);
}
void main()  
{  
    int i;  
    int a[] = {81,94,11,96,12,35,17,95,28,58,41,75,15};   
    quickSort(a, 13);  
    for(i = 0; i < 13; i++)  
        printf("%d ", a[i]);  
}
//以第一个元素作为基准,单向划分的快速排序
#include<stdio.h>
void Swap(int *a, int *b)
{
    int Tmp = *a;
    *a = *b;
    *b = Tmp;
}
void Qsort(int A[], int Left, int Right)
{
    int i, j, Pivot;
 
    if(Left >= Right)
        return;
    Pivot = A[Left];
    int pos = Left;
    for(i = Left+1; i <= Right; ++i)
    {
	if(A[i] < Pivot)
	    Swap(&A[++pos], &A[i]);
    }
    Swap(&A[pos], &A[Left]);

    Qsort(A, Left, pos - 1);
    Qsort(A, pos + 1, Right);
}
void quickSort(int A[], int N)
{
    Qsort(A, 0, N - 1);
}









你可能感兴趣的:(几种排序算法)