C语言的7大排序(超详细)

一、排序的基本概念

排序的概念:

所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作

稳定性:

假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次
序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排
序算法是稳定的;否则称为不稳定的。

二、排序的分类及实现

在这里插入图片描述

2.1直接插入排序

直接插入排序就是把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
eg:打牌摸牌插入的过程
在这里插入图片描述

特征总结:
1.元素集合越接近有序,直接插入的算法效率越高
2. 时间复杂度:O(N^2) ,最好情况是:O(N)
3. 空间复杂度:O(1),它是一种稳定的排序算法
4. 稳定性:稳定

代码:

void InsertSort(int* a, int n)
{
    for (int i = 0; i < n - 1; i++)
    {
        int end = i;
        int tmp = a[i + 1];
        //tmp不能为下标,不然会在插入过程中被覆盖
        while (end >= 0)
        {
            if (a[end] > tmp)
            {
                a[end + 1] = a[end];
                end--;
            }
            else
            {
                break}
        }
        a[end + 1] = tmp;
    }
}

2.2希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个
组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工
作。当到达=1时,所有记录在统一组内排好序。

希尔排序又基于插入排序:
·插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;
·但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位;
在这里插入图片描述
特征总结:
1.希尔排序是对直接插入排序的优化
2. 当gap>1时都是预排序,目的是让数组更接近有序,当gap=1时,数组已经接近有序,这样会很快。
3. 希尔排序的时间复杂度很不好计算
4. 稳定性:不稳定

void ShellSort(int* a, int n)
{
    int gap = n;
    //1.gap>1 预排序
    //2.gap==1 直接排序
    while (gap > 1)//判断条件不能为gap>=1 ,否则gap = 1会陷入死循环
    {
        gap = gap / 3 + 1;
        for (int i = 0; i <n-gap; i++)
        {
      
            int end = i;
            int tmp = a[i + gap];
            while (end >= 0)
            {
                if (a[end] > tmp)
                {
                    a[end + gap] = a[end];
                    end -= gap;
                }
                else
                {
                    break;
                }
            }
            a[end + gap] = tmp;

        }
     
    }

}

2.3选择排序

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的
数据元素排完 。
无论什么数据进去都是 O(n^2) 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。
在这里插入图片描述

特征总结:

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定
void SelectSort(int* a, int n)
{
    int begin = 0, end = n - 1;
    int mini = begin, maxi = begin;

    while (begin < end)
    {
        for (int i = begin; i <= end; i++)
        {
            if (a[mini] > a[i])
            {
                mini = i;
            }
            if (a[maxi] < a[i])
            {
                maxi = i;
            }
        }

        Swap(&a[begin], &a[mini]);
        if (maxi == begin)
        {
            maxi = mini;
        }
        Swap(&a[end], &a[maxi]);

        begin++;
        end--;
    }
}

2.4堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是
通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

在这里插入图片描述
特征总结:

  1. 堆排序使用堆来选数,效率就高了很多。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定
void AdjustDown(int* a, int n, int parent)
{
    int child = parent * 2 + 1;
    while (child < n)
    {
    //child+1 可能会越界
        if (child + 1 < n && a[child + 1] > a[child])
        {
            child++;
        }

        if ( a[child] > a[parent])
        {
            Swap(&a[child], &a[parent]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
           break;
        }
    }
}

void HeapSort(int* a, int n)
{
    for (int i = (n - 1 - 1) / 2; i >= 0; i--)
    {
        AdjustDown(a, n, i);
    }

    int end = n - 1;
    while(end >0)
    {
        Swap(&a[0], &a[end]);
        AdjustDown(a, end, 0);
        end--;
    }
}

2.5冒泡排序

基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排
序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
在这里插入图片描述

特性总结:

  1. 冒泡排序是一种非常容易理解的排序
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定
void Bubble(int* a, int n)
{
    for (int i = 0; i < n; i++)
    {
        bool exchange = false;
        for (int j = i; j < n - i - 1; j++)
        {
            if (a[j] > a[j + 1])
            {
                Swap(&a[j], &a[j + 1]);
                exchange = true;
            }
            else
            {
                continue;
            }
        }
        if (exchange = true)
        {
            break;
        }
    }
}

2.6快速排序

其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

在这里插入图片描述
特性总结:

  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(logN)
  4. 稳定性:不稳定
void PartSort1(int* a, int begin, int end)
{
    int keyi = begin;
    while (begin < end)
    {
        //再来一次begin<end判断防止越界
        while (begin < end && a[keyi] < a[end])
        {
            end--;
        }

        while (begin < end && a[keyi] > a[begin])
        {
            begin++;
        }
        Swap(&a[begin], &a[end]);

    }
    Swap(&a[keyi], &a[begin]);
    return begin;
}

void QuickSort(int* a, int begin, int end)
{
    if (begin >= end)
    {
        //1.区间只有一个
        //2.区间不存在
        return;
    }

    int keyi = PartSort1(a, begin, end);
    //[begin,keyi-1]keyi[keyi+1,end]
    PartSort1(a, begin, keyi - 1);
    PartSort1(a, keyi+1, end);

}

2.7归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and
Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有
序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
在这里插入图片描述

void _MergeSort(int* a, int begin,int end, int* tmp)
{
    if (begin == end)
    {
        return;
    }
    int mid = (begin + end) / 2;
    //[begin,mid][mid+1,end]
    _MergeSort(a, begin, mid, tmp);
    _MergeSort(a, mid+1, end, tmp);
    int begin1 = begin, end1 = mid;
    int begin2 = mid + 1, end2 = end;
    int i = begin;
    while (begin1 <= end1 && begin2 <= end2)
    {
        if (a[begin1] < a[begin2])
        {
            tmp[i++] = a[begin1];
        }
        else
        {
            tmp[i++] = a[begin2];
        }
    }

    while (begin1 <= end1)
    {
        tmp[i++] = a[begin1];
    }

    while (begin2 <= end2)
    {
        tmp[i++] = a[begin2];

    }
    memcpy(a + begin, tmp + begin, sizeof(int)*(end - begin + 1));
}

void MergeSort(int* a, int n)
{
    int* tmp = (int*)malloc(sizeof(int) * n);
    _MergeSort(a, 0, n - 1, tmp);
    free(tmp);
}

注:动态图借助别的博客

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值