数据结构-八大排序算法

最近在面试过程中,发现很多公司还是很注重基础的,对于基本的排序要求掌握的比较扎实,能够任意写出各种排序,对各种排序性能进行比较,那就随手总结一下吧。

排序分类一览

排序总览

类型一 交换排序

冒泡排序

特点:稳定,每一次都会有一个数字到最终的位置
冒泡排序-bubbleSort

void swap(int &a,int &b)
{
    int c = a;
    a = b;
    b = c;
}
void bubbleSort(int a[],int num)
{
    for(int i=0;i<num-1;i++)
        for(int j=0;j<num-i-1;j++)
            if(a[j]>a[j+1])swap(a[j],a[j+1]);
}

快速排序

特点:不稳定,每次中间的数字会有序,如果数据量太大的话,可能会爆栈。
快排具体一趟-quickSort1
快排整体-quickSort2

int qivotPosition(int a[],int low,int high)
{
    int temp = a[low];
    while(low<high)
    {
        while(low<high&&a[high]>=temp)high--;
        a[low] = a[high];
        while(low<high&&a[low]<=temp)low++;
        a[high] = a[low];
    }
    a[low] = temp;
    return low;
}

void quickSort(int a[],int low,int high)
{
    if(low<high)
    {
        int qivot = qivotPosition(a,low,high);
        quickSort(a,low,qivot-1);
        quickSort(a,qivot+1,high);
    }
}

比较

排序方法 平均时间复杂度 空间复杂度 稳定性
冒泡排序 O(n2) O(1)
快速排序 O(nlogn) O(logn)

类型二 插入排序

直接插入排序

特点:稳定,前面的一部分总是有序的,数据量比较小的时候适用。
直接插入-directInsertSort

void directInsert(int a[],int num)
{
    int temp;
    for(int i=0; i<num-1; i++)
    {
        if(a[i]>a[i+1])
        {
            int j;
            temp = a[i+1];
            for(j=i; temp<a[j]&&j>=0; j-- )
                a[j+1] = a[j];
            a[j+1] = temp;
        }
    }
}

希尔排序

特点:不稳定,跨步长有序
希尔排序-shellSort

void shellSort(int a[],int num)
{
    int temp;
    for(int dk=num/2; dk>=1; dk/=2)
        for(int i=dk; i<=num; i++)
         {
            if(a[i]<a[i-dk])
            {
                temp = a[i];
                int j;
                for(j=i-dk;temp<a[j]&&j>=0;j-=dk)
                    a[j+dk] = a[j];
                a[j+dk] = temp;
            }
         }
}

比较

排序方法 平均时间复杂度 空间复杂度 稳定性
直接插入排序 O(n2) O(1)
希尔排序 O(n1.3) O(1)

类型三 选择排序

简单选择排序

特点:不稳定,每次都能有一个有序
简单选择排序-simpleSelectSort

void selectSort(int a[],int num)
{
    for(int i=0;i<num-1;i++)
    {
        int min = i;
        for(int j=i+1;j<num;j++)
            if(a[j]<a[min])min = j;  //记录最小值
        if(min!=i)
            swap(a[i],a[min]);
    }
}

堆排

特点:堆排分为大顶堆和小顶堆,本例是以大顶堆为例,每次调整以后都可以找到最大值。最为重要的是堆排在最坏的情况下也可以保证O(nlogn)的时间复杂度,且空间复杂度为O(1)。数据量较大的时候比较适合选择。
Note本例中的数据有特殊性 a[1]-a[num] 其中a[0]为临时存储容器
步骤如下

1.首先将序列构造成大根堆(位于根节点的一定是当前序列的最大值)

构建大根堆

2.取出当前大顶堆的根节点,将其与序列末尾元素进行交换
3.对交换后的n-1个序列元素进行调整,使其满足大顶堆的性质

调整大根堆

4.重复2.3步骤,直至堆中只有1个元素为止

void adjustDown(int a[],int k,int num);
void buildMaxHeap(int a[],int num)
{
    for(int i=num/2;i>0;i--)
        adjustDown(a,i,num);
}
void adjustDown(int a[],int k,int num)
{
    a[0] = a[k];
    for(int i=2*k;i<=num;i*=2)
    {
        if(i<num&&a[i]<a[i+1])
            i++;
        if(a[0]>=a[i])break;
        else
        {
            a[k] = a[i];
            k = i;
        }
    }
    a[k] = a[0];
}
void heapSort(int a[],int num)
{
    buildMaxHeap(a,num);
    for(int i=num;i>1;i--)
    {
        swap(a[i],a[1]);
        adjustDown(a,1,i-1);
    }
}

比较

排序方法 平均时间复杂度 空间复杂度 稳定性
简答选择排序 O(n2) O(1)
堆排序 O(nlogn) O(1)

类型四 归并排序

归并排序

特点:稳定,唯一速度快的排序中稳定的排序算法,但付出了空间的代价。局部有序,进而全局有序。
归并排序-mergeSort

int b[100];
void merge(int a[],int low,int mid,int high)
{
    int i,j,k;
    for(k=low;k<=high;k++)
    {
        b[k] = a[k];
    }
    for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++)
    {
        if(b[i]<=b[j])
            a[k] = b[i++];
        else
            a[k] = b[j++];
    }
    while(i<=mid) a[k++] = b[i++];               //若第一个表未检测完,复制
    while(j<=high) a[k++] = b[j++];              //若第二个表未检测完,复制
}
void mergeSort(int a[],int low,int high)
{
    if(low<high)
    {
        int mid = (low+high)/2;
        mergeSort(a,low,mid);
        mergeSort(a,mid+1,high);
        merge(a,low,mid,high);
    }
}

比较

排序方法 平均时间复杂度 空间复杂度 稳定性
归并排序 O(nlogn) O(n)

类型五 基数排序

基数排序

步骤如下:

1.分配:将L[i]中的元素取出,首先确定个位上的数字,根据该数字分配到与之序号相同的桶中。
2.收集:当序列中所有的元素都分配到对应的桶中,再按照顺序依次将桶中的元素收集成新的一个待排序的序列L[];
3.对新形成的序列L[]重复执行分配和收集工作,对元素中的十位、百位…直到分配完该序列中的最高位,则排序结束。总体应用了一种“桶排”的思想。

比较

排序方法 平均时间复杂度 空间复杂度 稳定性
基数排序 O(d(n+r))

代码实现

上述排序的完整的可执行版本:

#include <iostream>

using namespace std;
//交换排序
//冒泡 稳定 每一次都会有一个数字到最终的位置
void swap(int &a,int &b)
{
    int c = a;
    a = b;
    b = c;
}
void bubbleSort(int a[],int num)
{
    for(int i=0;i<num-1;i++)
        for(int j=0;j<num-i-1;j++)
            if(a[j]>a[j+1])swap(a[j],a[j+1]);
}

//快排 不稳定 每次中间的数字会有序
//数据量中等时选用 数量太大可能会爆栈

int qivotPosition(int a[],int low,int high)
{
    int temp = a[low];
    while(low<high)
    {
        while(low<high&&a[high]>=temp)high--;
        a[low] = a[high];
        while(low<high&&a[low]<=temp)low++;
        a[high] = a[low];
    }
    a[low] = temp;
    return low;
}

void quickSort(int a[],int low,int high)
{
    if(low<high)
    {
        int qivot = qivotPosition(a,low,high);
        quickSort(a,low,qivot-1);
        quickSort(a,qivot+1,high);
    }
}

//插入排序
//直接插入 稳定 前面的一部分总是有序的
//数据量比较小的时候适用

void directInsert(int a[],int num)
{
    int temp;
    for(int i=0; i<num-1; i++)
    {
        if(a[i]>a[i+1])
        {
            int j;
            temp = a[i+1];
            for(j=i; temp<a[j]&&j>=0; j-- )
                a[j+1] = a[j];
            a[j+1] = temp;
        }
    }
}
//希尔排序 不稳定 跨步长有序
//步长的起始值为num/2

void shellSort(int a[],int num)
{
    int temp;
    for(int dk=num/2; dk>=1; dk/=2)
        for(int i=dk; i<=num; i++)
         {
            if(a[i]<a[i-dk])
            {
                temp = a[i];
                int j;
                for(j=i-dk;temp<a[j]&&j>=0;j-=dk)
                    a[j+dk] = a[j];
                a[j+dk] = temp;
            }
         }
}
//选择排序 不稳定
//简单选择排序 每次都能有一个有序
void selectSort(int a[],int num)
{
    for(int i=0;i<num-1;i++)
    {
        int min = i;
        for(int j=i+1;j<num;j++)
            if(a[j]<a[min])min = j;  //记录最小值
        if(min!=i)
            swap(a[i],a[min]);
    }
}

//堆排  因为最后的交换 所以也是不稳定排序
//分为大顶堆和小顶堆 本例以大顶堆为例 每次调整之后都可以找到最大值
//本例中的数据有特殊性 a[1]-a[num] 其中a[0]为临时存储容器 所以数组声明需要a[num+1]
void adjustDown(int a[],int k,int num);
void buildMaxHeap(int a[],int num)
{
    for(int i=num/2;i>0;i--)
        adjustDown(a,i,num);
}
void adjustDown(int a[],int k,int num)
{
    a[0] = a[k];
    for(int i=2*k;i<=num;i*=2)
    {
        if(i<num&&a[i]<a[i+1])
            i++;
        if(a[0]>=a[i])break;
        else
        {
            a[k] = a[i];
            k = i;
        }
    }
    a[k] = a[0];
}
void heapSort(int a[],int num)
{
    buildMaxHeap(a,num);
    for(int i=num;i>1;i--)
    {
        swap(a[i],a[1]);
        adjustDown(a,1,i-1);
    }
}
//归并排序 稳定排序 局部有序,进而全局有序
int b[100];
void merge(int a[],int low,int mid,int high)
{
    int i,j,k;
    for(k=low;k<=high;k++)
    {
        b[k] = a[k];
    }
    for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++)
    {
        if(b[i]<=b[j])
            a[k] = b[i++];
        else
            a[k] = b[j++];
    }
    while(i<=mid) a[k++] = b[i++];               //若第一个表未检测完,复制
    while(j<=high) a[k++] = b[j++];              //若第二个表未检测完,复制
}
void mergeSort(int a[],int low,int high)
{
    if(low<high)
    {
        int mid = (low+high)/2;
        mergeSort(a,low,mid);
        mergeSort(a,mid+1,high);
        merge(a,low,mid,high);
    }
}

int main()
{
    int num;
    int a[100];
    /*
    //堆排的输入输出
    while(cin>>num)
    {
        for(int i=1;i<=num;i++)
            cin>>a[i];
        heapSort(a,num);
        for(int i=1;i<=num;i++)
            cout<<a[i]<<" ";
        cout<<endl;

    }*/


    while(cin>>num)
    {
    //cin>>num;  //一个数组的长度
    for(int i=0;i<num;i++)
    {
        cin>>a[i];
    }
    //bubbleSort(a,num);
    //quickSort(a,0,num-1);
    //directInsert(a,num);
    //shellSort(a,num);
    //selectSort(a,num);
    mergeSort(a,0,num-1);
    for(int i=0;i<num;i++)
        cout<<a[i]<<" ";
    cout<<endl;

    }


    return 0;
}

性能对比

性能对比-性能总体比较
其中有几个算法比较特殊,归并排序是唯一一个效率较高,却稳定的算法,因为它付出了空间的代价;堆排序与归并的最好、最差时间复杂度都可以是O(nlogn),且堆排的空间复杂度仅为O(1)。

猜你喜欢

转载自blog.csdn.net/BigBrick/article/details/85312927