常见的排序

名称

复杂度

说明

备注

冒泡排序
Bubble Sort

O(N*N)

将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮

 

插入排序

Insertion sort

O(N*N)

逐一取出元素,在已经排序的元素序列中从后向前扫描,放到适当的位置

起初,已经排序的元素序列为空

选择排序

O(N*N)

首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此递归。

 

快速排序

Quick Sort

O(n *log2(n))

先选择中间值,然后把比它小的放在左边,大的放在右边(具体的实现是从两边找,找到一对后交换)。然后对两边分别使用这个过程(递归)。

 

堆排序Heap Sort

O(n *log2(n))

利用堆(heaps)这种数据结构来构造的一种排序算法。堆是一个近似完全二叉树结构,并同时满足堆属性:即子节点的键值或索引总是小于(或者大于)它的父节点。

近似完全二叉树

希尔排序

SHELL

O(n1+)

0<£<1

选择一个步长(Step) ,然后按间隔为步长的单元进行排序.递归,步长逐渐变小,直至为1.

 

箱排序
Bin Sort

O(n)

设置若干个箱子,把关键字等于 k 的记录全都装入到第 k 个箱子里 ( 分配 ) ,然后按序号依次将各非空的箱子首尾连接起来 ( 收集 ) 。

分配排序的一种:通过 " 分配 " 和 " 收集 " 过程来实现排序。

桶排序

Bucket Sort

O(n)

桶排序的思想是把 [0 , 1) 划分为 n 个大小相同的子区间,每一子区间是一个桶。

 

1.插入排序

稳定
空间复杂度O(1)
时间复杂度O(n^2)
最差情况:反序,需要移动n*(n-1)/2个元素
最好情况:正序,不需要移动元素

void insertsort(int a[], int n)
{
    int i, j;
    int tmp;

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

 

2.冒泡排序

稳定
空间复杂度O(1)
时间复杂度O(n^2)
最差情况:反序,需要交换n*(n-1)/2个元素
最好情况:正序,不需要交换元素

void bubblesort(int a[], int n)
{
    int i, j;
    int tmp;

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

 

3.选择排序

不稳定
空间复杂度O(1)
时间复杂度O(n^2)
最差情况:第一个元素为最大元素,其余元素正序,需要交换n-1个元素(例如:4 3 2 1)
最好情况:正序,不需要交换元素

void selectsort(int a[], int n)
{
    int i, j, k;
    int tmp;

    for (i = 0; i < n - 1; i++)
    {
        k = i;
        for (j = i + 1; j < n; j++)
        {
             if (a[j] < a[k])
                 k = j;
        }
        if (k != i)
        {
            tmp = a[i];
            a[i] = a[k];
            a[k] = tmp;
        }
    }
}

 

4.希尔排序(缩小增量排序)

不稳定
空间复杂度O(1)
时间复杂度优于插入排序,最坏情况下O(n^2)
最差情况:有n=2^k个元素,偶数位置上有n/2个同为最大的元素,奇数位置上有n/2个同为最小的元素,此时最优最后一趟进行插入排序(例如:1 5 2 6 3 7 4 8)
最好情况:正序,不需要移动元素

void shellsort(int a[], int n)
{
    int i, j, increment;
    int tmp;
    for (increment = n / 2; increment > 0; increment /= 2)
    {
        for (i = increment; i < n; i++)
        {
            tmp = a[i];
            for (j = i - increment; j >= 0 && tmp < a[j]; j -= increment)
            {
                a[j + increment] = a[j];
            }
            a[j + increment] = tmp;
        }
    }
}

5.堆排序

不稳定
空间复杂度O(1)
时间复杂度O(nlogn)

#define leftchild(i) (2 * (i) + 1)

void percdown(int a[], int i, int n)
{
    int child;
    int 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;
    int tmp;

    for (i = n / 2; i >= 0; i--)
    {
        percdown(a, i, n);
    }
    for (i = n - 1; i > 0; i--)
    {
        tmp = a[0];
        a[0] = a[i];
        a[i] = tmp;
        percdown(a, 0, i);
    }
}

6.归并排序

稳定
空间复杂度O(n)
时间复杂度O(nlogn)

void merge(int a[], int tmparray[], 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])
            tmparray[tmppos++] = a[lpos++];
        else
            tmparray[tmppos++] = a[rpos++];
    }

    while (lpos <= leftend)
        tmparray[tmppos++] = a[lpos++];
    while (rpos <= rightend)
        tmparray[tmppos++] = a[rpos++];

    for (i = 0; i < numelements; i++, rightend--)
        a[rightend] = tmparray[rightend];
}

void msort(int a[], int tmparray[], int left, int right)
{
    int center;

    if (left < right)
    {
        center = (left + right) / 2;
        msort(a, tmparray, left, center);
        msort(a, tmparray, center + 1, right);
        merge(a, tmparray, left, center + 1, right);
    }
}

void mergesort(int a[], int n)
{
    int *tmparray;

    tmparray = (int*)malloc(n * sizeof(int));
    if (tmparray != NULL)
    {
        msort(a, tmparray, 0, n - 1);
        free(tmparray);
    }
    else
        printf("No space for tmp array!!!/n");
}

7.快速排序(当小于4个元素时,用到插入排序)

不稳定
空间复杂度O(1)
时间复杂度O(nlogn)
最差情况:要排序的数组基本有序,枢纽每次取最大(小)元素,退化为冒泡算法
最好情况:枢纽两边元素个数基本相同

#define cutoff (3)

void swap(int *a, int *b)
{
    int tmp;
    tmp = *a;
    *a = *b;
    *b = 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;
    int pivot;

    if (left + cutoff <= 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
        insertsort(a + left, right - left + 1);
}

void quicksort(int a[], int n)
{
    qsort(a, 0, n - 1);
}

 

 

各种排序方法比较

     简单排序中直接插入最好(稳定、易于实现),快速排序最快,当文件为正序时,直接插入和冒泡均最佳。

    (1)若n较小(如n≤50),可采用直接插入或直接选择排序。
当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插人,应选直接选择排序为宜。
    (2)若文件初始状态基本有序(指正序),则应选用直接插人、冒泡或随机的快速排序为宜;
    (3)若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序。
快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。若要求排序稳定,则可选用归并排序。但本章介绍的从单个记录起进行两两归并的排序算法并不值得提倡,通常可以将它和直接插入排序结合在一起使用。先利用直接插入排序求得较长的有序子文件,然后再两两归并之。因为直接插入排序是稳定的,所以改进后的归并排序仍是稳定的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值