冒泡排序、选择排序、插入排序、堆排序

        本文主要讲解几种数据结构当中常用的排序算法,本文的例子与代码都以实现升序排序为基础。

首先,给出实现排序的基础交换两个值的函数

void Swap(int* a, int* b)//交换函数
{
    int ret = *a; 
    *a = *b; 
    *b = ret;
    return;
}

一. 冒泡排序

1. 实现思想:

        遍历要排序的数列,每次遍历都要依次进行比较,一次比较两个元素,如果顺序与预期不一致,就进行交换,直至一次遍历结束。


2. 时间复杂度:O(n^2)

3. 空间复杂度:O(1)

4. 稳定性:稳定排序

5. 实现代码:

//1.冒泡排序:从后向前冒,每次把最小的放在前面
void BubbleSort(int arr[], size_t size)
{
    if(size <= 1)//元素个数不用排序
        return;
    //[0,bound)表示有序元素,[bound,size)表示待排序元素
    size_t bound = 0;
    for(; bound<size; ++bound)
    {   
        size_t cur = size - 1;
        for(; cur>bound; --cur)
        {   
            if(arr[cur] < arr[cur-1])
                Swap(&arr[cur], &arr[cur-1]);
        }   

    }   
}

二. 选择排序

1. 实现思想

        以升序为例,遍历要排序的数列,每次遍历找出最小的元素将其与此次遍历到的元素交换,遍历结束,排序也结束。


2. 时间复杂度:O(n^2)

3. 空间复杂度:O(1)

4. 稳定性:不稳定排序

5. 实现代码:

//2.选择排序:升序
void SelectSort(int arr[], size_t size)
{
    if(size <= 1)
        return;
    size_t bound = 0;
    for(; bound<size; ++bound)
    {
        size_t cur = bound + 1;
        for(; cur < size; ++cur)
        {
            if(arr[bound] > arr[cur])
                Swap(&arr[bound], &arr[cur]);
        }
    }
}

三. 插入排序

1. 实现思想

        把有序区间(从一个元素开始)当作线性表,依次遍历待排序元素,寻找合适位置,插入到有序区间中。


2. 时间复杂度:O(n^2)

3. 空间复杂度:O(1)

4. 稳定性:稳定排序

5. 实现代码:

//3.插入排序
//把有序区间当作线性表,把bound所指元素插入到这个有序的线性表中
void InsertSort(int arr[], size_t size)
{
    if(size <= 1)
        return;
    //1.定义好边界,[0,bound)表示有序的线性表
    size_t bound = 1;
    for(; bound<size; ++bound)
    {
        //2.保存当前bound所指的元素
        int value = arr[bound];//保存是为了搬运
        size_t cur = bound;//cur辅助搬运,
        //3.从后向前去找一个合适的放置当前bound所指的元素
        for(; cur>0; --cur)
        {
            if(arr[cur-1] > value)//搬运
            {
                arr[cur] = arr[cur-1];
            }
            else//已找到合适位置
                break;
        }
        //该句不能写在else控制的语句中
        //因为若当前bound所指元素前面所以有都比它大,此时要插入到数组的第一个元素,但是此时循环结束了,执行不了该句赋值
        arr[cur] = value;//插入bound所指元素
    }
}

四. 堆排序

1. 实现思想

        在之前介绍堆的相关操作实现博客中,介绍过堆排序的思想。这里实现思想是一样的,就是依次删除堆顶元素,直至删完,排序结束。不够要注意的是:大堆实现升序,小堆实现降序。

2. 时间复杂度:O(n*logn)

3. 空间复杂度:O(1)

4. 稳定性:不稳定排序

5. 实现代码:

        这里有上浮式调整和下沉式调整两种,具体意义可见上面贴的堆的实现博客。

//4.堆排序
void AdjustDown(int arr[], size_t size, size_t index)//下沉式调整函数
{
    size_t parent = index;
    size_t child = 2*parent + 1;
    while(child < size)
    {//建大堆
        if(child+1 < size && arr[child] < arr[child+1])
            child = child + 1;
        //此时child指向左右子树较大的一个
        if(arr[parent] < arr[child])
            Swap(&arr[parent], &arr[child]);
        parent = child;
        child = 2*parent + 1;
    }
}

void AdjustUp(int arr[], size_t size, size_t index)//上浮式调整函数
{
    size_t child = index;
    size_t parent = (child-1)/2;
    while(child > 0)
    {//建大堆
        if(arr[parent] < arr[child])
            Swap(&arr[parent], &arr[child]);
        child = parent;
        parent = (child-1)/2;
    }
}
void HeapCreate(int arr[], size_t size)//创建堆
{//大堆->升序
    if(size <= 1)
        return;
    //下沉式调整
    //从后向前遍历,起始位置是堆从后向前第一个非叶子结点                                                                                                                
    size_t i = (size-1-1)/2;//最后一个元素的父结点就是起始位置
    for(; i>0; --i)
    {
        AdjustDown(arr, size, i);
    }
    AdjustDown(arr, size, 0);
    //上浮式调整
    //从前向后遍历,起始位置为数组首元素
    //for(i=0; i<size;++i)
    //{
    //    AdjustUp(arr, i+1,i);
    //}
}

void HeapPop(int arr[], size_t size)//删除堆顶元素
{
    if(size <= 1)
        return;
    Swap(&arr[0], &arr[size-1]);
    AdjustDown(arr, size-1, 0);
}

void HeapSort(int arr[], size_t size)//堆排序
{
    if(size <= 1)
        return;
    //创建堆
    HeapCreate(arr, size);
    //循环删除堆顶元素
    size_t i = 0;
    for(; i<size; ++i)
    {
        HeapPop(arr, size-i);//第二个参数表示当前数组中哪部分满足堆的规则
        //第一次删除前,[0,size)是堆;
        //第二次删除前,[0,size-1)是堆;
        //第三次删除前,[0,size-2)是堆...
    }
    return;
}




猜你喜欢

转载自blog.csdn.net/Lycorisradiata__/article/details/80484463