您的位置:首页 > 理论基础 > 数据结构算法

【数据结构与算法基础】几种典型的排序算法 / Typical sort algorithms

2011-04-19 19:51 555 查看
// 所有原创文章转载请注明作者及链接
// blackboycpp(AT)gmail.com
// QQ群: 135202158









几种典型的排序算法:冒泡排序、插入排序、桶排序(基数排序前面的文章有,这里不实现了)、希尔排序、堆排序、归并排序,快速排序。

所有算法均使用ANSI C实现。





/********************************************************************
	File: 		sort.h	
	Author:		blackboy  blackboycpp@gmail.com
	Purpose:	常见的各种排序算法,C实现
	Created:	2011-04-15
	Modified:	2011-04-19   19:43
*********************************************************************/

#ifndef  __SORT_H__
#define  __SORT_H__

void  BubbleSort(int A[], int N);
void  InsertionSort(int A[], int N);
void  BucketSort(int A[], int N);
void  ShellSort(int A[], int N);
void  HeapSort(int A[], int N);
void  MergeSort(int A[], int N);
void  QuickSort(int A[], int N);

#endif








/*
   关于各种排序算法的参考资料,见 
   http://en.wikipedia.org/wiki/Sorting_algorithms */

#include <stdlib.h>
#include <stdio.h>
#include "sort.h"

/* 冒泡排序  http://en.wikipedia.org/wiki/Bubble_sort  
最差时间复杂度	O(N^2)
最优时间复杂度	O(N)
平均时间复杂度	O(N^2)

    冒泡排序由N-1趟排序组成。在第i趟排序后,将前N-i+1个元素中的最大值放到N-i位置,比如,第1趟
排序把N个元素中的最大值放到最后一个位置N-1上。在第i趟排序时,依次将第0到第N-i-1个元素和它的下一
个元素进行比较,把更大的元素放在后面。
    可以在第2个循环中加入标记来检测是否发生了元素交换。如果没有发生交换,则证明所有元素已序。
*/
void  BubbleSort(int A[], int N)
{
    int i, j, tmp;

    for(i=0; i<N-1; ++i)
    {
        for(j=0; j<N-i-1; ++j)
        {
            if(A[j] > A[j+1])
            {
                tmp = A[j];
                A[j] = A[j+1];
                A[j+1] = tmp;
            }
        }
    }
}

/* 插入排序  http://en.wikipedia.org/wiki/Insertion_sort 
最差时间复杂度	O(N^2)
最优时间复杂度	O(N)
平均时间复杂度	O(N^2)

    插入排序由N-1趟排序组成,对于第i趟排序,把第i个元素移动到前i+1个元素中正确的位置上,从而
保证0到i位置上的i+1个元素有序。
    有意思的是当所有元素已序时,第2个循环将自动停止。
*/
void  InsertionSort(int A[], int N)
{
    int i, j, tmp;

    for(i=1; i<N; ++i)
    {
        tmp = A[i];
        for(j=i; j>0 && A[j-1] > tmp; --j)
            A[j] = A[j-1];
        A[j] = tmp;
    }
}

/* 桶排序  http://en.wikipedia.org/wiki/Bucket_sort 
最差时间复杂度	O(N^2)
最优时间复杂度	O(N + K)
平均时间复杂度   O(N * K)

    设待排序的序列A中的元素大小为0-M。先构造一个大小为(M+1)的桶B,并将桶中所有元素置为0。依次
扫描A中的元素,对于A[i],将B[A[i]]增1。最后,将不为空桶依次输出。
*/

#define K 10

void  BucketSort(int A[], int N)
{
    int Backet[K] = {0};
    int i, j;
    
    for(i=0; i<N; ++i)
        Backet[A[i]]++;
    for(i=0; i<K; ++i)
        for(j=0; j<Backet[i]; ++j)
            printf("%d ", i);
    printf("/n");
}

/* 希尔排序  http://en.wikipedia.org/wiki/Shell_sort 
最差时间复杂度	根据增量序列的不同而不同,O(N log^2 N)
最优时间复杂度	O(N)
平均时间复杂度	根据增量序列的不同而不同

    希尔排序使用一个增量序列(increment sequence): h1, h2,...,ht. 其中h1 = 1。在使用增量hk的
一趟排序后,保证所有的 A[i] <= A[i+hk],即所有相隔hk的元素是有序的。
    以下代码中使用Shell建议的增量序列ht = N/2, hk = [h(k+1)/2],但这个序列没有Hibbard增量或
Sedgewick增量好。
*/
void  ShellSort(int A[], int N)
{
    int i, j, k, tmp; // k为增量, 对于N=10, 为5,2,1

    for(k = N/2; k>0; k /= 2)
        for(i=k; i<N; ++i)
        {
            tmp = A[i];
            for(j=i; j>=k; j -= k)
            {
                if(tmp < A[j-k])
                    A[j] = A[j-k];
                else
                    break;
            }
            A[j] = tmp;
        }
}

/* 堆排序  http://en.wikipedia.org/wiki/Heapsort 
最差时间复杂度	O(N logN)
最优时间复杂度	Ω(N), O(N logN)
平均时间复杂度	O(N logN)

    使用优先队列,确切地说是一个(max)堆。首先对要排序的数组建立堆(BuildHeap),然后执行N次
DeleteMax(),并将删除的元素放在堆的最后一个元素的下一位置。
*/

// 这里和二叉堆实现不太一样,实际元素从下标0开始,而不是1
#define  LeftChild(i)  (2*(i) + 1)

// 向下过滤,使大元素向上浮动
static 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(tmp < A[Child])
            A[i] = A[Child];
        else
            break;
    }
    A[i] = tmp;
}

void  HeapSort(int A[], int N)
{
    int i, tmp;

    // BuildHeap, 这是一个(max) Heap
    for(i=N/2; i>=0; --i)
        PercDown(A, i, N);

    // N次DeleteMax
    for(i=N-1; i>0; --i)
    {
        tmp = A[0];
        A[0] = A[i];
        A[i] = tmp;
        PercDown(A, 0, i);
    }
}

/* 归并排序  http://en.wikipedia.org/wiki/Merge_sort 
最差时间复杂度	O(N logN)
最优时间复杂度	O(N), natural variant
平均时间复杂度	O(N logN)

    归并排序的基本操作是合并两个已排序的表。它采取了典型的分治(divide-and-conquer)策略,递归地
将要排序的表分成两部分,各自排序,然后再将这两个已序的表合并。
*/
static void  Merge(int A[], int Temp[], int LPos, int RPos, int RightEnd)
{
    int i, LeftEnd, Counts, TempPos;

    LeftEnd = RPos - 1;
    TempPos = LPos;
    Counts = RightEnd - LPos + 1;

    // 依次比较两个表中的元素,将较小的元素放入临时表
    while(LPos <= LeftEnd && RPos <= RightEnd)
    {
        if(A[LPos] <= A[RPos])
            Temp[TempPos++] = A[LPos++];
        else
            Temp[TempPos++] = A[RPos++];
    }

    // 至此,左、右两表必有一个有剩余元素,将这些剩余元素复制到临时表
    while(LPos <= LeftEnd)
        Temp[TempPos++] = A[LPos++];
    while(RPos <= RightEnd)
        Temp[TempPos++] = A[RPos++];

    // 将已序的临时表拷贝回原表
    for(i=0; i<Counts; i++, RightEnd--)
        A[RightEnd] = Temp[RightEnd];
}

static void  MSort(int A[], int Temp[], int Left, int Right)
{
    int Center;

    if(Left < Right)
    {
        Center = (Left + Right) / 2;
        MSort(A, Temp, Left, Center);
        MSort(A, Temp, Center+1, Right);
        Merge(A, Temp, Left, Center+1, Right);
    }
}

void  MergeSort(int A[], int N)
{
    int*  Temp;

    Temp = (int*)malloc(sizeof(int) * N);
    MSort(A, Temp, 0, N-1);
    free(Temp);
}

/* 快速排序  http://en.wikipedia.org/wiki/Quicksort 
最差时间复杂度	O(N^2)
最优时间复杂度	O(N logN)
平均时间复杂度	O(N logN)

    快速排序主要由以下几步构成(设A为待排序的数组):
    1. 如果A中的元素个数为0或1,返回;
    2. 从A中选取一个枢纽元Pivot,比较好的办法是“三数中值分割法”,即从A的左、中、右端各取
       一个元素,将3个数的中值做为Pivot;
    3. 将A的其余元素根据Pivot分成两部分A1和A2,A1的所有元素都小于Pivot,A2则大于等于Pivot;
    4. 递归地对A1和A2分别进行排序。
*/
static void Swap(int* pa, int* pb)
{
    int tmp = *pa;
    *pa = *pb;
    *pb = tmp;
}

// 三数中值分割法, 取左、中、右3个元素将对其排序,然后将中间的元素放入右元素的前一个
// 位置(Right-1)
static 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];
}

#define  Cufoff  3

static void QSort(int A[], int Left, int Right)
{
    int i, j, Pivot;

    if(Left+Cufoff <= Right)
    {
        Pivot = Median3(A, Left, Right);
        i = Left; j = Right-1;

        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);
}










#include <stdlib.h>
#include <memory.h>
#include <stdio.h>
#include "sort.h"

#define  N   12

const int array
 = {34, 8, 64, 85, 32, 21, 52, 9, 111, 6, 40, 23};
const int array2
 = {3, 1, 9, 0, 7, 8, 2, 5, 4, 6, 3, 2}; // 用于桶排序的小数字数组

void RestoreArray(int A[])
{
    memcpy(A, array, N * sizeof(int));
}

void PrintArray(int A[])
{
    int i;
    for(i=0; i<N; ++i)
        printf("%d ", A[i]);
    printf("/n");
}

int main()
{
    int A
;

    RestoreArray(A);
    BubbleSort(A, N);
    PrintArray(A);

    RestoreArray(A);
    InsertionSort(A, N);
    PrintArray(A);

    RestoreArray(A);
    ShellSort(A, N);
    PrintArray(A);

    RestoreArray(A);
    HeapSort(A, N);
    PrintArray(A);

    RestoreArray(A);
    MergeSort(A, N);
    PrintArray(A);

    RestoreArray(A);
    QuickSort(A, N);
    PrintArray(A);

    BucketSort(array2, N);

    system("PAUSE");
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: