数据结构之“七大排序“

本文详细介绍了排序算法的原理和应用场景,包括内部排序与外部排序的区别,稳定性概念。重点讲解了直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序和归并排序的实现细节及其特性。针对每种排序算法,讨论了其时间复杂度、空间复杂度和稳定性。此外,还提到了排序算法的优化策略,如快速排序的“三数取中”和小区间优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 排序的概念和应用

1.1、排序的概念

  1. 排序:什么是排序?排序就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作
  2. 内部排序:数据元素全部放在内存中的排序
  3. 外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序
  4. 稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的

请添加图片描述


1.2、排序的应用

  1. 比如:京东上面的综合排序和价格排序

在这里插入图片描述

  1. 比如:高校之间热度的排序

在这里插入图片描述

  1. 还有我常见的"点外卖",我们一般都会点热度最高的店铺,还有就是我们在学校中考试成绩的排序等等…

1.3、常见的排序算法

在这里插入图片描述


2. 插入排序

2.1、直接插入排序

  1. 直接插入排序其基本思想是:
    把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列
  1. 实际中我们玩扑克牌时,就用了插入排序的思想…

在这里插入图片描述


直接插入排序(单趟)过程分析:

  1. 当插入第(i>=1)个元素时,前面的end[0],end[1],end[2]…end[i-1]已经排好序
  2. 这时,我们将保存end[i]的值,跟end[i-1],end[i-2]…end[0]进行比较
  3. 当end[i]的值比待比较的值小时,将end[i-1]的值覆盖到end[i],然后–end
  4. 当比待比较的值小时,跳出循环,然后直接插入到end[i]位置.
    请添加图片描述

代码实现

void InsertSort(int* p, int n)
{
   
   
    for (int i = 0; i < n - 1; ++i)
    {
   
   
        int end = i;
        //保存end下标后面的数据
        int tmp = p[end + 1];
        //当end为
        while (end >= 0)
        {
   
   
            //升序,判断tmp是否小于p[end]
            if (tmp < p[end])
            {
   
   
                //小于则覆盖end后面的数据
                p[end + 1] = p[end];
                --end; //end往前移
            }
            else
            {
   
   
                break;
            }
        }
        //当end走到-1时,放在循环里面处理会导致数组越界访问
        p[end + 1] = tmp;
    }
}

直接插入排序的特性总结:

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

2.2、希尔排序

希尔排序:希尔排序又称"缩小增量法"。
希尔排序的基本思想是:

  • 先选定一个整数的间隔gap,把待排序文件中所有的数据分成一个个组,所有距离为gap的数据分在同一组内,并对每一组内的数据进行排序。
  • 随后,我们将重复上述的分组和排序。
  • 当gap为1时,就是直接插入排序了,最后,所有数据都在统一组内排好序了。
  • 希尔排序其实是插入排序的变形,希尔每次走gap步(预排序),而插入排序走1步。

请添加图片描述

预排序:每次间隔为gap,直到i < 0时,才进行下一轮的预排序。每次进行间隔gap / 3 + 1的预排。当gap为1时,说明数据已经接近有序,直接进行插入排序

代码实现:

void ShellSort(int* p, int n)
{
   
   
    int gap = n;
    while (gap > 1)
    {
   
   
        //比如有1000个数,gap之间的间隔为"334",第二次间隔为112次,第三次间隔为"38",第四次为"13",第五次为"5", 第六次为"2"(前面为"预排序"),最后为"1"进行(插入排序)
        //当gap > 1时,进行预排序-----当gap等于1时,说明已经接近有序,进行"插入排序"
        //一开始进行排序时,i必须小于n - gap,如果i < n时,会导致tmp赋值时,会导致越界
        for (int i = 0; i < n - gap; ++i)
        {
   
   
            int end = i;
            int tmp = p[end + gap];
            while (end >= 0)
            {
   
   
                if (tmp < p[end])
                {
   
   
                    p[end + gap] = p[end];
                    end -= gap;
                }
                else
                {
   
   
                    break;
                }
            }
            p[end + gap] = tmp;
        }
    }
}

注意:gap每次走几步是没有规定的,可以每次走gap/2步,甚至是每次走gap/5+1步,控制好最后gap为1就行

希尔排序的特性总结:

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些书中给出的希尔排序的时间复杂度都不固定
  4. 时间复杂度:因为代码中的gap是按照"Knuth"提出的方式取值的,而且Knuth进行了大量的试验统计,所以时间复杂度就暂时按照:O(N1.25)到 O(1.6*N1.25)来算
  5. 空间复杂度:O(1)
  6. 稳定性:不稳定

3. 选择排序

3.1 选择排序

基本思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完

选择排序过程解析:

  • 在元素集合array[i]-----array[n-1]中选择最大(小)的数据元素
  • 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
  • 在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,直到集合剩余1个元素
    请添加图片描述
    代码实现:
void SelectSort(int* p, int n)
{
   
   
    //找数组中最大值和最小值,然后交换到最左边和最右边里面
    int left = 0;
    int right = n - 1;
    while (left < right)
    {
   
   
        int Max = right, Min = left;
        for (int i = left; i <= right; ++i)
        {
   
   
            //在左闭右闭[left, right]区间中找最大值和最小值
            if (p[i] > p[Max])
                Max = i;

            if (p[i] < p[Min])
                Min = i;
        }
        //交换数据
        Swap(&p[left], &p[Min]);

        //如果left和Max重叠,需修正Max
        if (left == Max)
            Max = Min;

        Swap(&p[right], &p[Max])
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值