八大排序算法与复杂度

八大排序

  在处理大批量数据时,有序化的数据可以在很大程度上提高算法效率。
直接插入排序 先总结一下数据结构的八大排序,分别是插入排序中的直接插入排序希尔排序,交换排序中的起泡排序快速排序,选择排序中的直接选择排序堆排序,以及归并排序基数排序
  如何评价排序的优劣呢?除了正确,易读和容错(自动检错,报错并通过与用户对话来纠错)以外,性能是一个重要指标。

性能分析

  算法性能是指运行一个算法所需要的时间长短和内存多少,他们分别称为时间复杂性空间复杂性

 时间复杂性分析

  1)有些计算机需要用户提供程序运行时间的上限。一旦达到这个上限,程序将被强制结束。
  2)一个正在开发的程序可能需要一个令人满意的实时响应。
  选择什么样的时间单位(程序步)来度量算法运行时间呢?对少量的输入,算法瞬间就运行完了。所以对算法性能的评价总是对大的输入量而言的。
  假设输入量是n,算法运行时间是n的函数T(n),我们研究当很大时,T(n)是什么级别。这里就用到了大O记法:如果存在正常数c和n0,使得当n≥n0时,T(n)≤ c*f(n),则记为T(n)=O(f(n))。

 空间复杂性分析

  算法所需空间包括固定部分和变动部分。固定部分与输入量或规模无关,主要包括程序码空间和常量,变量和对象的定长所占的空间。变动部分与输出量有关,主要包括递归栈空间和中间处理所需空间。如果用P表示算法,S(P)表示空间需求,那么S(P)=c(固定部分)+Sp(变动部分)。算法的空间复杂性分析重点是变动部分Sp

 稳定性

  此外,如果一种排序实施前后,关键码相同的任意两个数据元素其前后次序没有发生变化,那么这个排序方法就被称作是稳定的,否则就是不稳定的

1.直接插入排序

template
void InsertSort(T* pa, int n)
{
    T temp;
    for(int i = 1; i < n; i++)
    {
        temp = pa[i];
        int j = i;
        while(j >= 1 && temp < pa[j-1])
        {
            pa[j] = pa[j-1];
            j--;
        }
        pa[j] = temp;
    }
}

原理:从待排序集的第1个数据元素开始,依次选择数据元素,与有序子集的数据元素依次从后往前进行比较,选择插入位置。

稳定性:稳定

2.希尔排序

template
void ShellSort(T* pa, int n)
{
    T temp;
    int gap = n/2;
    while(gap)
    { 
        for(int start = gap; start < 2*gap && start < n; start ++)
            for(int i = start; i < n; i += gap)
            {
                temp = pa[i];
                int j = i;
                while(j >= gap && temp < pa[j - gap])
                {
                    pa[j] = pa[j - gap];
                    j -= gap;
                }
                pa[j] = temp;
            }
        gap = gap/2;
    }
}

原理:以增量为步长划分子序列,即同一子序列的数据元素,其下标步长等于增量。对每个子序列实施直接插入排序。不断缩小增量,当增量为1时,所有数组元素都在一个子序列中,成为有序集。
   通俗来讲,增量即为数组中元素下表的差值,假设步长为4,及a[0],a[4],a[8]…为一个子序列。实行直接插入排序后,将增量缩小为一半,直至增量缩小为1。

稳定性:不稳定

3.起泡排序

template
void BubbleSort(T* pa, int n)
{
    T temp;
    int i = 0;
    while(i < n - 1)
    {
        int last = n - 1;
        for(int j = n - 1; j > i; j --)
            if(pa[j] < pa[j-1])
            {
                temp = pa[j - 1];
                pa[j - 1] = pa[j];
                pa[j] = temp;
                last = j;
            }
        i = last;
    }
 }

原理:把数组分为左右两个半区,左半区为有序子集,右半区为无序子集。开始时,左半区为空。在无序子集中,从后往前,两两相邻元素比较,逆序则交换。最后交换的位置成为有序子集的上界。直到一趟起泡排序中没有发生交换,排序停止。

稳定性:稳定

4.快速排序

template  //给数组分区
void Partition(T* pa, int low, int high)
{
    int i = low, j = high;
    T temp = pa[i];
    while(i != j)
    {
        while(pa[j] >= temp && j > i)
            j --;
        if(j > 1)
            pa[i ++] = pa[j];
        while(pa[i] <= temp && i
void QuickSort(T* pa, int low, int high)
{
    if(low >= high)
        return;
    int m = Partition(pa, low, high);
    QuickSort(pa, low, m - 1);
    QuickSort(pa, m + 1,high);
}

template
void QuickSort(T* pa, int n)
{
    QuickSort(pa, 0, n - 1);
}

原理:取无序子集中的第一个数据元素作为基准,将无序子集分为左右两个半区,左半区不大于基准,右半区不小于基准;然后对左右半区重复上述操作,知道各半区元素个数为1.

稳定性:不稳定,主要是划分算法Partition造成的。

5.直接选择排序

template
void SelectSort(T* pa, int n)
{
    T temp;
    for(int i = 0; i <= n - 1; i ++)
    {
        int min = i;
        for(int j = i + 1; j < n; j ++) 
            if(pa[j] < pa[min])
                min = j;
            if(min != i);
        {
            temp = pa[i];
            pa[i] = pa[min];
            pa[min] = temp;
        }
    }
}

原理:将数组分为左右两个半区,左半区为有序子集,右半区为无序子集。开始时,有序子集为空。在无序子集中,选出最小元素,与无序子集第一个元素交换,再将第一个元素并入有序子集中。重复上述操作。

稳定性:稳定

6.堆排序

template
void BuildHeap(T* pa, int size)  //建大根堆
{
    for(int i = size/2 - 1; i >= 0; i--)
        PercolateDown(pa, i, size);  ////将下标[i, size)范围内的元素调整为堆
}

template  
void PercolateDown(T* pa, int pos, int size)  //将下标[pos, size)范围内的元素调整为堆
{
    int p = pos,c = 2*p + 1;
    T temp = pa[p];
    while(c < size)
    {
        if(c + 1 < size && pa[c + 1] > pa[c])
            ++ c;
        if(temp >= pa[c])
            break;
        else
        {
            pa[p] = pa[c];
            p = c;
            c = 2*p + 1;
        }
    }
    pa[p] = temp;
}

template
void HeapSort(T* pa, int n)
{
    T temp;
    BuildHeap(pa, n);
    for(int i = n - 1; i > 0; i --)
    {
        temp = pa[0];
        pa[0] = pa[i];
        pa[i] = temp;
        PercolateDown(pa, 0, i);
    }
}

原理:
1)将数组分为左右两个半区,左半区为有序子集,右半区为无序子集。开始时,有序子集为空。
2)将无序子集创建为大根堆。
3)将堆化为无序子集首位数据元素交换,将交换后的尾元素并入有序子集,然后把缩小的无序子集调整为大根堆。
4)重复步骤3)n-2次。

稳定性:不稳定

7.归并排序

template
void Merge(T* ini,T* merge, int s, int m, int e)  //二路归并
{
    int i = s, j = m + 1, k = s;
    while(i <= m && j <= e)
        if(ini[i] < ini[j])
            merge[k ++] = ini[i ++];
        else
            merge[k ++] = ini[j ++];
    if(i <= m)
        while(i <= m)
            merge[k ++] = int[i ++];
    else
        while(j <= e)
            merge[k ++] = ini[j ++];
}

template
void MergePass(T* ini,T* merge, int len, int size)  //一趟归并
{
    int i = 0;
    while(i + 2*len - 1 <= size - 1)
    {
        Merge(ini, merge, i, i + len - 1, i + 2 * len - 1);
        i = i + 2*len;
    }
    if(i + len <= size - 1)
        Merge(ini, merge, i, i + len - 1, size - 1);
    else
        while(i <= size - 1)
            merge[i ++] = int[i];
}

template
void MergeSort(T* pa, int n)  //迭代的归并排序
{
    T* merge = new T[n];
    int len = 1;
    while(len < n)
    {
        MergePass(pa, merge, len, n);
        len *= 2;
        MergePass(merge, pa, len, n);
        len *= 2;
    }
    delete[]merge;
}

原理:
1)归并(一般指二路归并):将两个有序表合成一个新的有序表。包含关键码的原始数组ini分为左右两个有序分区(归并段)[s,m]和[m+1,e],将他们按序归并,一个归并段存储到一个辅助数组(merge)中。
2)迭代归并:包含关键码的原始数组ini按长度len划分为几个连续的归并段,每一个归并段都有序,用二路归并将相邻归并段合成一个长度为2len的归并段并存入辅助数组,这个过程称为一趟归并。重复上述步骤。
 ①剩下一个长度为len的归并段和一个长度不足len的归并段,继续调用二路归并。
 ②只剩下一个长度为len或不足len的归并段,直接移至辅助数组merge。

稳定性:稳定

8.基数排序

include 
void RadixSort(int* pa, int n)
{
    queue Q[10];
    int base = 1, flag = 1, k;
    while(flag)
    {
        for(int i = 0; i < n; i ++)
        {
            k = (pa[i]/base) % 10;
            Q[k].push(pa[i]);
        }
        base *= 10;
        flag = 0;
        i = 0;
        for(k = 0; k < 10; k ++)
            while(!Q[k].empty())
            {
                pa[i++] = Q[k].front();
                Q[k].pop();
                if(pa[i - 1]/base != 0 && flag == 0)
                    flag = 1;
            }
    }
}

原理:采用“分配”和“收集”技术,从关键码的低位到高位进行比较。有十个队列作为分配用的”箱子“,编号0~9。遵照先进先出原则,从个位开始排序,到十位,百位,以此类推。

稳定性:稳定

你可能感兴趣的:(八大排序算法与复杂度)