C语言实现-排序1

news2024/9/20 18:34:51

在这里插入图片描述

文章目录

  • 🎯引言
  • 👓排序
    • 1.排序的概念以及运用
      • 1.1概念
      • 1.2运用
      • 1.3常见的排序算法
    • 2.排序算法的实现
      • 2.1插入排序
        • 2.1.1直接插入排序
        • 2.1.2希尔排序
      • 2.2选择排序
        • 2.2.1直接选择排序
        • 2.2.2堆排序
  • 🥇结语

🎯引言

欢迎来到HanLop博客的C语言数据结构初阶系列。在本篇文章中,我们将探讨一种基础而又极为重要的算法——排序(Sorting)。排序算法在计算机科学中占据着举足轻重的地位,它们被广泛应用于各种场景,如数据检索、数据库管理、图像处理等。通过排序,我们可以将数据按照一定的顺序进行组织和管理,从而提高程序的效率和可读性。在本文中,我们将介绍几种经典的排序算法,包括冒泡排序、直接选择排序、堆排序、直接插入排序、希尔排序、快速排序和归并排序,并通过代码示例,展示如何在C语言中实现这些算法。无论您是初学者还是有经验的程序员,这篇文章都将帮助您理解排序算法的基本原理及其在实际编程中的应用。

👓排序

1.排序的概念以及运用

1.1概念

排序是指将一组无序的数据按照特定的规则重新排列,使其形成有序序列的过程。排序的目标是通过一定的比较和交换规则,将数据按升序或降序排列,便于后续的查找、插入、删除等操作。

1.2运用

排序算法的应用场景非常广泛,即使在日常生活中也能找到许多例子。以下是一些普通人也能轻松理解的例子:

  1. 书籍整理: 当你将家中的书籍按字母顺序排列时,你实际上是在对书籍进行排序。这使得你可以更快速地找到某本书,因为你知道每本书在书架上的大致位置。
  2. 考试成绩排名: 在学校中,老师通常会将学生的考试成绩从高到低排列,生成一个成绩单。这是一个典型的排序应用,通过这个排序,可以轻松确定每个学生的排名。

1.3常见的排序算法

本次博客只讲解插入和选择排序

在这里插入图片描述

2.排序算法的实现

2.1插入排序

2.1.1直接插入排序

直接插入排序是一种简单的排序算法,适用于小规模的数据集。它的基本思想是通过逐步将元素插入到已排序的部分,逐渐形成有序的序列。以下是对你提供的直接插入排序函数的详细解析:

//代码实现
void InsertSort(int* arr, int n)
{
    int i = 0;
    // 外层循环:逐步将每个元素插入到已排序部分
    for (i = 0; i < n - 1; i++)
    {
        int end = i;            // end 指向已排序部分的最后一个元素
        int num = arr[end + 1]; // 记录当前要插入的元素
        
        // 内层循环:将当前元素插入到已排序部分的适当位置
        while (end >= 0)
        {
            // 如果已排序部分的元素大于当前元素,将其向后移动
            if (arr[end] > num)
            {
                arr[end + 1] = arr[end];
                end--;
            }
            else
            {
                break; // 一旦找到比当前元素小的位置,跳出循环
            }
        }

        // 将当前元素插入到合适的位置
        arr[end + 1] = num;
    }
}

解析:

  1. 函数定义
    • void InsertSort(int* arr, int n):该函数接受一个整数数组 arr 以及数组的长度 n,并对数组进行直接插入排序。
  2. 外层循环 (for (i = 0; i < n - 1; i++)):
    • 这个循环从第一个元素开始,逐步将每个元素插入到已排序部分。注意,i从0开始,遍历到 n-2(即第 n-1 个元素),因为在 in-1 时,整个数组就已经排序完成。
  3. 已排序部分的最后一个元素 (int end = i):
    • end 指向当前已排序部分的最后一个元素位置。它表示我们需要将当前 arr[i+1] 插入到从 arr[0]arr[i] 的已排序部分。
  4. 待插入的元素 (int num = arr[end + 1]):
    • num 存储当前需要插入到已排序部分的元素值,即 arr[i+1]
  5. 内层循环 (while (end >= 0)):
    • 这个循环用于找到 num 在已排序部分中的正确位置。每次比较 num 与已排序部分中的元素 arr[end],如果 arr[end]num 大,那么我们需要将 arr[end] 向后移动,以便为 num 腾出空间。
    • 条件判断 (if (arr[end] > num)):
      • 如果 arr[end]num 大,说明 num 还没有找到合适的位置,arr[end] 向后移动 (arr[end + 1] = arr[end]),并继续向前比较 (end--)。
    • 循环终止条件 (else { break; }):
      • 一旦找到 arr[end] <= num 的情况,说明 num 应该插入到 arr[end] 后面的位置,循环终止。
  6. 插入元素 (arr[end + 1] = num):
    • 最后,将 num 插入到正确的位置 arr[end + 1]。到此,已排序部分的长度增加1,即包含从 arr[0]arr[i+1] 的元素。

时间复杂度分析

直接插入排序的时间复杂度主要取决于数组中元素的初始排列情况。我们可以从以下几种情况进行分析:

  1. 最佳情况(已排序数组)
    • 如果数组已经是有序的,那么在每次插入时,内层循环 while (end >= 0) 不会执行(因为 arr[end] <= num),只需进行一次比较。因此,每次插入操作的时间复杂度是O(1),整个数组的排序时间复杂度为O(n)。
  2. 最坏情况(逆序数组)
    • 如果数组是逆序排列的,那么在每次插入时,内层循环需要比较并移动所有已排序的元素。例如,当插入第 i+1 个元素时,需要进行 i+1 次比较和 i+1 次移动。因此,最坏情况下的时间复杂度为O(n^2)。
  3. 平均情况
    • 在随机排列的数组中,插入第 i+1 个元素时,内层循环需要大约 i/2 次比较和移动。因此,平均情况下的时间复杂度为O(n^2)。

时间复杂度总结:

  • 最佳情况:O(n)
  • 最坏情况:O(n^2)
  • 平均情况:O(n^2)

空间复杂度分析

直接插入排序是一种原地排序算法,它只需要常数级别的额外空间来存储临时变量 num。无论数组的大小如何,所需的额外空间都不变,因此空间复杂度为O(1)。

空间复杂度总结:

  • 空间复杂度:O(1)

结论

直接插入排序的时间复杂度在最坏和平均情况下都是O(n^2),但在最佳情况下是O(n)。空间复杂度为O(1),意味着它不需要额外的存储空间。这使得直接插入排序适用于小规模的数据集或近乎有序的数组,但在大规模或完全无序的数据集上表现较差。

2.1.2希尔排序

希尔排序是一种基于插入排序的排序算法,它通过逐步减少间隔(gap)来比较和交换不相邻的元素,最终实现整个数组的有序。希尔排序也被称为缩小增量排序(Diminishing Increment Sort),是插入排序的更高效改进版本。以下是对你提供的希尔排序函数的详细解析:

//代码实现
void ShellSort(int* arr, int n)
{
    int gap = n;
    // 外层循环:逐步减少间隔 gap
    while (gap > 1)
    {
        // 缩小间隔,通常选择 gap = gap / 3 + 1
        gap = gap / 3 + 1;
        // 内层循环:对每个间隔 gap 进行插入排序 实现多组并排的效果
        for (size_t i = 0; i < n - gap; i++)
        {
            int end = i;
            int num = arr[end + gap]; // 待插入的元素

            // 类似于插入排序,按当前间隔 gap 对元素进行排序
            while (end >= 0)
            {
                if (arr[end] > num)
                {
                    arr[end + gap] = arr[end]; // 向后移动元素
                    end -= gap; // 按间隔跳跃比较
                }
                else
                {
                    break; // 找到正确位置后跳出循环
                }
            }

            // 插入元素到正确位置
            arr[end + gap] = num;
        }
    }
}

解析:

  1. 函数定义
    • void ShellSort(int* arr, int n):该函数接受一个整数数组 arr 和数组长度 n,并使用希尔排序算法对数组进行排序。
  2. 初始化间隔 (int gap = n):
    • gap 初始值为数组长度 n。间隔决定了当前比较和交换的元素之间的距离。
  3. 外层循环 (while (gap > 1)):
    • 外层循环用于逐步缩小间隔 gap,直到 gap 小于等于1时结束。gap 的缩减采用 gap = gap / 3 + 1,这是一种常见的选择,可以保证每次缩减间隔时依然覆盖较大的范围。
  4. 内层循环 (for (size_t i = 0; i < n - gap; i++)):
    • 内层循环用于对每个间隔 gap 进行插入排序。i 表示当前已排序的元素位置,i + gap 是待插入元素的索引。
  5. 待插入的元素 (int num = arr[end + gap]):
    • num 存储当前间隔 gap 后待插入的元素值,即 arr[i + gap]
  6. 插入排序的过程 (while (end >= 0)):
    • 这一部分与直接插入排序类似,只是每次比较和移动的元素间隔为 gap,而不是相邻的元素。
    • 条件判断 (if (arr[end] > num)):
      • 如果 arr[end] 大于 num,则将 arr[end] 向后移动到 arr[end + gap],并且 end 减少 gap 继续比较。
    • 循环终止条件 (else { break; }):
      • 一旦找到比 num 小的元素位置,说明 num 应该插入到 arr[end + gap],跳出循环。
  7. 插入元素 (arr[end + gap] = num):
    • num 插入到正确的位置 arr[end + gap]
  8. 最后一次排序 (gap = 1):
    • gap 减小到1时,希尔排序退化为直接插入排序,对所有相邻元素进行一次最终的排序。此时,整个数组就已经基本有序了,插入排序的效率较高。

由于希尔排序的时间复杂度涉及一些非常复杂的数学知识,我们这里就不继续深入探讨,几个结论,希尔排序的时间复杂度大约是O(n1.5)

2.2选择排序

2.2.1直接选择排序

直接选择排序(Selection Sort)是一种简单直观的排序算法,它通过多次扫描未排序的部分,每次从中找到最大值或最小值,将其放到已排序部分的末尾或开头,逐步构建有序数组。以下是你提供的直接选择排序函数的详细解析:

void SelectSort(int* arr, int n)
{
    int left = 0;
    int right = n - 1;

    int maxi = left, mini = left;

    while (left < right)
    {
        // 找到当前未排序部分的最大值和最小值
        for (int i = left + 1; i <= right; i++)
        {
            if (arr[i] < arr[mini])
            {
                mini = i; // 更新最小值的位置
            }

            if (arr[i] > arr[maxi])
            {
                maxi = i; // 更新最大值的位置
            }
        }

        // 将最小值交换到未排序部分的开头
        // 如果最大值在最小值的位置,先更新最大值的位置
        if (maxi == left)
        {
            maxi = right;
        }
        Swap(&arr[mini], &arr[left]);
        Swap(&arr[maxi], &arr[right]);

        // 缩小范围
        left++;
        right--;
    }
}

解析:

  1. 函数定义
    • void SelectSort(int* arr, int n):该函数接受一个整数数组 arr 和数组长度 n,使用直接选择排序算法对数组进行排序。
  2. 初始化左右边界 (int left = 0, right = n - 1):
    • leftright 分别表示当前未排序部分的左边界和右边界。排序的目标是逐步将最小值移到左边界,将最大值移到右边界。
  3. 初始化最大值和最小值的位置 (int maxi = left, mini = left):
    • maximini 分别用于记录未排序部分中当前最大值和最小值的位置。初始时它们都指向未排序部分的左边界 left
  4. 主循环 (while (left < right)):
    • 主循环不断缩小未排序部分的范围,直到 leftright 相遇为止。每次迭代,确定当前范围内的最大值和最小值,然后将它们放在正确的位置。
  5. 内层循环 (for (int i = left + 1; i <= right; i++)):
    • 内层循环扫描当前未排序部分的每个元素,找出最小值和最大值的位置。minimaxi 在循环过程中不断更新。
  6. 处理最大值在左边界的特殊情况 (if (maxi == left)):
    • 如果最大值位于左边界 left,在交换最小值到左边界之前,需要提前更新 maxi 的位置为右边界 right。这是因为接下来会将最小值交换到左边界,可能会覆盖当前的 maxi
  7. 交换最小值和最大值
    • Swap(&arr[mini], &arr[left]):将最小值交换到左边界 left
    • Swap(&arr[maxi], &arr[right]):将最大值交换到右边界 right
  8. 缩小范围 (left++, right--):
    • 每次成功交换后,将左边界 left 向右移一位,右边界 right 向左移一位,缩小未排序部分的范围,进入下一轮排序。

直接选择排序的时间复杂度分析

  1. 最坏情况下的时间复杂度
    • 比较次数:对于每一轮扫描,直接选择排序需要在未排序部分中找到最大值和最小值。对于第 i 轮,未排序部分包含 n - 2i 个元素,因此需要进行 n - 2i - 1 次比较。整个排序过程中的比较次数可以表示为: 比较次数 = ( n − 1 ) + ( n − 3 ) + ⋯ + 1 = ( n − 1 ) + 1 2 × n 2 ≈ n 2 4 比较次数=(n−1)+(n−3)+⋯+1 = \frac{(n-1) + 1}{2} \times \frac{n}{2} \approx \frac{n^2}{4} 比较次数=(n1)+(n3)++1=2(n1)+1×2n4n2
    • 所以,最坏情况下的时间复杂度为 O(n^2)。
  2. 平均情况下的时间复杂度
    • 平均情况下的比较次数与最坏情况类似,仍然是 O(n^2),因为直接选择排序的操作步骤与数据的初始排列无关,总是执行相同的比较和交换操作。
  3. 最佳情况下的时间复杂度
    • 即使数组已经有序,直接选择排序仍然需要扫描数组以确定最大值和最小值,因此最佳情况下的时间复杂度仍为 O(n^2)。

时间复杂度总结:

  • 最坏情况:O(n^2)
  • 平均情况:O(n^2)
  • 最佳情况:O(n^2)

直接选择排序的空间复杂度分析

直接选择排序是原地排序算法,不需要额外的存储空间来存储临时数据,除了几个用于交换和迭代控制的辅助变量。因此,空间复杂度是 O(1)。

空间复杂度总结:

  • 空间复杂度:O(1)
2.2.2堆排序

堆排序在我之前的博客有讲到,大家自行跳转到该博客去了解堆排序堆

🥇结语

通过本篇文章的学习,您应该已经掌握了插入排序和选择排序算法的基本原理及其在C语言中的实现方法。在下一章,我们将会讲解的是快速排序以及归并排序还有计数排序。排序是许多复杂算法的基础,理解这些基本算法不仅能够帮助您优化代码性能,还能为您在数据结构和算法的学习之路上奠定坚实的基础。我们鼓励您在实践中不断运用和优化这些算法,从而加深理解。在未来的文章中,我们将探讨更多高级的数据结构和算法,敬请期待!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1994759.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#如何对某个词在字符串中出现的次数进⾏计数(LINQ)

文章目录 基础知识实现方法基础计数LINQ优化处理标点符号总结 LINQ&#xff08;Language-Integrated Query&#xff09;是C#和VB.NET中强大的查询语言&#xff0c;它可以用来查询集合、SQL数据库、XML文档等。在C#中&#xff0c;我们可以使用LINQ来简化对字符串中特定单词出现次…

C语言实现游戏2048(超详细!!!超易懂!!!)

2048是众所周知的一款经典游戏&#xff0c;在曾经没有智能电脑和手机的年代&#xff0c;也陪伴了我们许多年。那今天就让我们用C语言来回顾一下这款游戏吧~ 一、游戏2048的思路 2048游戏的玩法是在初始的时候&#xff0c;给玩家一个4*4格子的&#xff0c;其中内容全为空的棋盘…

基于SpringBoot+Vue的供应商管理系统(带1w+文档)

基于SpringBootVue的供应商管理系统(带1w文档) 基于SpringBootVue的供应商管理系统(带1w文档) 现今&#xff0c;互联网在我们的日常生活占据着日益重要的地位&#xff0c;我们也越来越离不开对移动设备、电脑等上网设备的使用。传统的供应商管理系统模式主要依靠管理人员纯手工…

PyQt6简易案例代码GUI界面小工具——实现二维码生成器+自定义前后背景色(练手正合适)

目录 专栏导读PyQt6的介绍PyQt6的主要特点包括&#xff1a;使用PyQt6开发应用程序的一般步骤&#xff1a; 库的安装1、初始化与界面设计2、设置前景色、背景色功能完整代码总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双…

IP地址的构成

1. IPv4地址 IPv4地址是最早且目前仍然广泛使用的IP地址版本。由32位二进制数构成&#xff0c;应为32为二进制数太长了&#xff0c;所以我们通常用四个十进制数字来表示&#xff0c;每个数字之间用”.”分隔。这些数字的范围是0到255。IPv4地址的格式为&#xff1a; “A.B.C.…

2024世界机器人大会将于8月21日至25日在京举行

2024年的世界机器人大会预定于8月21日至25日&#xff0c;在北京经济技术开发区的北人亦创国际会展中心隆重举办。 本届大会以“共育新质生产力 共享智能新未来”为核心主题&#xff0c;将汇聚来自全球超过300位的机器人行业专家、国际组织代表、杰出科学家以及企业家&#xff0…

再启新征程——灵川县“灵秀山川”区域公共品牌发布会顺利举办

灵川县&#xff0c;自古便享有“楚越通衢&#xff0c;风气之先”的美誉&#xff0c;见证了无数文化的交流与融合。这里&#xff0c;土地肥沃&#xff0c;资源丰富&#xff0c;如同大自然的无尽宝库&#xff0c;孕育了琳琅满目的优质农特产品。立足于本地优势资源&#xff0c;灵…

Python | Leetcode Python题解之第329题矩阵中的最长递增路径

题目&#xff1a; 题解&#xff1a; class Solution:DIRS [(-1, 0), (1, 0), (0, -1), (0, 1)]def longestIncreasingPath(self, matrix: List[List[int]]) -> int:if not matrix:return 0rows, columns len(matrix), len(matrix[0])outdegrees [[0] * columns for _ in…

数字人直播间搭建教程比较:哪种方案更可行?

当前&#xff0c;数字人直播的应用潜力不断显现&#xff0c;各大中小型企业对其关注度和接受度持续上升&#xff0c;连带着各种数字人直播间搭建教程的阅读量也日益上涨。而不少创业者也因此发现了它所蕴含的市场需求和收益空间&#xff0c;并有了通过为企业搭建数字人直播间以…

三防平板定制化:驱动产业高效化发展的新动能

在数字化转型的浪潮中&#xff0c;三防平板作为一种坚固耐用、功能强大的移动设备&#xff0c;正逐渐成为各行各业提升效率、优化管理的关键工具。通过硬件和软件的定制化服务&#xff0c;三防平板不仅能满足特定行业的需求&#xff0c;更能在复杂的工作环境中展现出卓越的性能…

haproxy实验

目录 为什么要用haproxy&#xff1f; haproxy的基本部署实验&#xff1a; 环境准备&#xff1a; 详细步骤&#xff1a; haproxy-多进程与多线程实验&#xff1a; haproxy的全局global配置实验&#xff1a; 为什么要用haproxy&#xff1f; LVS&#xff1a;没有后端检测&a…

Linux学习笔记:Linux基础知识汇总(kill 进程-vi编辑检索-查看当前文件夹的大小-修复硬盘等)

常见指令 Linux 的 find 命令可以用于在指定目录下查找符合条件的文件或目录。find 命令的基本语法为&#xff1a; find [path] [expression]其中&#xff0c;path 指定要查找的目录路径&#xff0c;expression 指定查找条件。下面是一些常用的 find 命令用法和示例&#xff…

代理IP在社媒营销中的重要作用

伴随着互联网的发展&#xff0c;社交媒体成了人们日常生活中不可或缺的一部分。用户在社交媒体中的活跃度和对内容的分享促进信息内容在短期内迅速传播&#xff0c;使得社交媒体变成了店家推广与销售的重要途径。 随着社交媒体用户基数的不断扩大和社交平台功能的日益丰富&…

煤炭行业信息化运维方案:基于一体化监控管理平台的探讨

随着煤炭行业信息化和智能化进程的加速&#xff0c;煤炭企业面临着前所未有的运维挑战。如何在复杂多变的IT环境中确保系统的稳定运行&#xff0c;提高运维效率&#xff0c;降低运营成本&#xff0c;成为煤炭企业亟待解决的问题。本文将以煤炭行业信息化运维现状为背景&#xf…

C#如何将自己封装的nuget包引入到项目中

问题 自己封装好了一个nuget包&#xff0c;但是不想上传到外网&#xff0c;想局域网使用&#xff0c;有两种方案 搭建私有nuget仓库放到离线文件夹中直接使用 第一种方式请请参考proget安装 下面主要是第二种方式 准备 新建类库项目 using System;namespace ClassLibrary…

怎样才算精通 Excel?

最强AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频百万播放量https://aitools.jurilu.com/ 高赞回答很系统&#xff0c;但普通人这么学&#xff0c;没等精通先学废了&#xff01; 4年前&#xff0c;我为了学数据分析&#…

关于低代码这一技术的杂谈

一、探讨低代码的定义 “Low-Code”是什么&#xff1f;身为技术人员听到这种技术名词&#xff0c;咱们第一反应就是翻看维基百科 或者其他相关技术论文&#xff0c;咱们想看维基百科的英文介绍&#xff1a; A low-code development platform (LCDP) provides a development e…

Angiopep-2;脑靶向多肽;TFFYGGSRGKRNNFKTEEY;CAS:906480-05-5

【Angiopep-2简介】 Angiopep-2是一种由19个氨基酸组成的多肽&#xff0c;它能够与低密度脂蛋白受体相关蛋白1&#xff08;LRP1&#xff09;特异性结合&#xff0c;通过内吞方式进入脑组织。这种多肽因其与LRP1的亲和力以及对血脑屏障的穿透能力而受到广泛关注&#xff0c;被认…

IF=12.5!孟德尔随机化,GWAS玩出花 | 孟德尔随机化周报(7.25-7.31)

孟德尔随机化,Mendelian Randomization&#xff0c;简写为MR&#xff0c;是一种在流行病学领域应用广泛的一种实验设计方法&#xff0c;利用公开数据库就能轻装上阵写文章&#xff0c;甚至是高质量的论文。 孟德尔随机化通过引入一个称之为工具变量的中间变量&#xff0c;来分析…

【C++】vector习题

一、杨辉三角 class Solution { public:vector<vector<int>> generate(int numRows) {} }; 这里给你一个vector<vector<int>>类型 也就是说vector中的各个数据&#xff0c;存的是各个不同的vector 思路&#xff1a;先给vector开空间&#xff0c;然后…