排序算法C语言实现

news2025/6/6 22:54:21

算法概览

排序算法平均时间复杂度最坏时间复杂度空间复杂度稳定性适用场景
插入排序O(n²)O(n²)O(1)稳定小规模/基本有序
希尔排序O(n log n)O(n²)O(1)不稳定中等规模
冒泡排序O(n²)O(n²)O(1)稳定教学/小规模
堆排序O(n log n)O(n log n)O(1)不稳定大规模数据
选择排序O(n²)O(n²)O(1)不稳定小规模
快速排序O(n log n)O(n²)O(log n)不稳定通用场景
归并排序O(n log n)O(n log n)O(n)稳定大数据/外部排序
计数排序O(n+k)O(n+k)O(k)稳定整数/小范围数据

一、插入排序(Insertion Sort)

原理与特点

插入排序的工作方式类似于整理扑克牌:将未排序元素逐个插入到已排序序列的正确位置。它的优势在于处理小规模或基本有序数据时效率高。

void InsertSort(int* a, int n) {
    for(int i = 0; i < n - 1; i++) {
        int end = i;
        int tmp = a[end + 1];  // 待插入元素
        // 寻找插入位置并移动元素
        while (end >= 0) {
            if (a[end] > tmp) {
                a[end + 1] = a[end];  // 元素后移
                end--;
            } else break;
        }
        a[end + 1] = tmp;  // 插入元素
    }
}

特点分析

  • 稳定排序算法

  • 自适应:数据越有序,效率越高

  • 当n≤1000时效率优于复杂排序算法

二、希尔排序(Shell Sort)

原理与特点

希尔排序是插入排序的改进版,通过将数组分组进行插入排序,逐步减小分组间隔,最终实现整体有序。它突破了O(n²)的时间复杂度瓶颈。

void ShellSort(int* a, int n) {
    int gap = n;
    while(gap > 1) {
        gap = gap / 2;  // 动态缩小间隔
        for (int i = 0; i < n - gap; i++) {
            int end = i;
            int tmp = a[end + gap];
            // 组内插入排序
            while (end >= 0) {
                if (a[end] > tmp) {
                    a[end + gap] = a[end];
                    end -= gap;  // 跨步比较
                } else break;
            }
            a[end + gap] = tmp;
        }
    }
}

特点分析

  • 第一个突破O(n²)的排序算法

  • 间隔序列的选择影响效率(本例使用Knuth序列)

  • 中等规模数据排序的理想选择

三、冒泡排序

原理与特点

冒泡排序是最基础的排序算法之一,通过不断比较相邻元素并交换位置,使较大元素逐渐"浮"到数组末尾,如同气泡上升的过程。其名称就来源于这种元素移动的特性。

void BubbleSort(int* a, int n) {
    for(int j = 0; j < n; j++) {
        int exchange = 0;  // 交换标志位
        
        // 单趟冒泡:将最大元素移到末尾
        for (int i = 1; i < n - j; i++) {
            // 相邻元素比较交换
            if (a[i - 1] > a[i]) {
                Swap(&a[i - 1], &a[i]);
                exchange = 1;  // 标记发生交换
            }
        }
        
        // 提前终止优化:未发生交换说明已有序
        if (exchange == 0) break;
    }
}

关键优化点

  1. 提前终止机制

    • 通过exchange标志检测是否发生交换

    • 当某趟未发生交换时,说明数组已完全有序

    • 对基本有序数组效率显著提升

  2. 逐步缩小范围

    • 每完成一趟排序,待排序范围缩小一位

    • n - j确保已排序部分不再参与比较

四、堆排序(Heap Sort)

原理与特点

利用堆数据结构实现的排序算法,通过构建大顶堆,不断将堆顶元素(最大值)与末尾元素交换,然后调整剩余元素为堆。

// 向下调整堆
void AdjustDown(int* a, int n, int parent) {
    int child = parent * 2 + 1;
    while (child < n) {
        // 选择较大的子节点
        if (child + 1 < n && a[child] < a[child + 1]) 
            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--;
    }
}

特点分析

  • 原地排序,空间复杂度O(1)

  • 适合处理超大规模数据

  • 常用于优先级队列实现

 

五、快速排序(Quick Sort)

原理与特点

采用分治策略的排序算法,通过选取基准值将数组划分为两个子数组,递归排序子数组。

 经典实现(双指针法)

void QuickSort(int* a, int left, int right) {
    if (left >= right) return;
    
    if (right - left + 1 < 10) {  // 小区间优化
        InsertSort(a + left, right - left + 1);
    } else {
        int midi = GetMidi(a, left, right);
        Swap(&a[left], &a[midi]);  // 基准值放最左
        
        int keyi = left;
        while (left < right) {
            while (left < right && a[right] >= a[keyi]) right--;
            while (left < right && a[left] <= a[keyi]) left++;
            Swap(&a[left], &a[right]);
        }
        Swap(&a[left], &a[keyi]);  // 基准值归位
        QuickSort(a, begin, left - 1);  // 递归左子数组
        QuickSort(a, left + 1, end);    // 递归右子数组
    }
}

 前后指针法(更高效)

void QuickSort2(int* a, int left, int right) {
    if (left >= right) return;
    
    int keyi = left;
    int prev = left, cur = left + 1;
    while (cur <= right) {
        if (a[cur] < a[keyi] && ++prev != cur)
            Swap(&a[prev], &a[cur]);
        cur++;
    }
    Swap(&a[keyi], &a[prev]);  // 基准值归位
    QuickSort2(a, left, prev - 1);
    QuickSort2(a, prev + 1, right);
}

六、归并排序(Merge Sort)

原理与特点

采用分治策略的稳定排序算法,将数组递归分割,然后合并有序子数组。本文提供递归和非递归两种实现。

void _MergeSort(int* a, int begin, int end, int* tmp) {
    if (begin >= end) return;
    
    int mid = (begin + end) / 2;
    // 递归分割
    _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) {
        tmp[i++] = a[begin1] <= a[begin2] 
                 ? a[begin1++] 
                 : 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 = malloc(sizeof(int) * n);
    _MergeSort(a, 0, n - 1, tmp);
    free(tmp);
}

特点分析

  • 稳定排序算法

  • 适合外部排序(大数据文件)

  • 链表排序的最佳选择

七、计数排序

void CountSort(int* a, int n) {
    // 确定数据范围
    int min = a[0], max = a[0];
    for (int i = 1; i < n; i++) {
        if (a[i] > max) max = a[i];
        if (a[i] < min) min = a[i];
    }
    int range = max - min + 1;
    
    // 创建计数数组
    int* count = malloc(sizeof(int) * range);
    memset(count, 0, sizeof(int) * range);
    
    // 统计元素频次
    for (int i = 0; i < n; i++) 
        count[a[i] - min]++;
    
    // 回写排序结果
    int j = 0;
    for (int i = 0; i < range; i++) {
        while (count[i]--) 
            a[j++] = i + min;
    }
    free(count);
}

适用场景

  • 数据范围为有限整数

  • 数据范围远小于数据量(k << n)

  • 需要稳定排序的场景

八、如何选择排序算法

  1. 小规模数据:插入排序

  2. 通用场景:快速排序(需随机化基准)

  3. 内存受限环境:堆排序

  4. 稳定排序需求:归并排序

  5. 已知数据范围:计数排序

  6. 链表排序:归并排序

  7. 外部排序:多路归并排序

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

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

相关文章

Python----目标检测(训练YOLOV8网络)

一、数据集标注 在已经采集的数据中&#xff0c;使用labelImg进行数据集标注&#xff0c;标注后的txt与原始 图像文件同名且在同一个文件夹&#xff08;data&#xff09;即可。 二、制作数据集 在data目录的同目录下&#xff0c;新建dataset目录&#xff0c;以存放制作好的YOLO…

构建 MCP 服务器:第一部分 — 资源入门

什么是模型上下文协议? 模型上下文协议(MCP) 是Claude等大型语言模型 (LLM) 与外部数据和功能安全交互的标准化方式。您可以将其想象成一个平视显示器,或者 AI 的 USB 端口——它提供了一个通用接口,允许任何兼容 MCP 的 LLM 连接到您的数据和工具。 MCP 提供了一个集中式协…

使用ZYNQ芯片和LVGL框架实现用户高刷新UI设计系列教程(第十五讲)

这一期讲解lvgl中日历控件的基础使用&#xff0c;Calendar 部件是一个经典日历&#xff0c;它具有以下功能&#xff1a;• 通过一个7x7矩阵显示任何月份 • 显示日期名称 • 突出显示当前日期&#xff08;今天&#xff09; • 突出显示任何用户定义的日期 日历是一个可编辑的小…

Vue中实现表格吸底滚动条效果,列太多时左右滚动条始终显示在页面中

1、安装 npm install el-table-horizontal-scroll 2、全局注册&#xff08;main.js&#xff09; import horizontalScroll from el-table-horizontal-scrollVue.use(horizontalScroll) 如下图&#xff0c;在main.js加上上面的代码 3、表格内引用 <el-table :data"…

BeeWorks 协同办公能力:局域网内企业级协作的全场景重构

在企业数字化办公场景中&#xff0c;BeeWorks 以强大的协同办公能力&#xff0c;将局域网内的通讯、协作、业务流程整合为统一整体。作为专注于企业级局域网环境的协作平台&#xff0c;其不仅提供即时通讯基础功能&#xff0c;更通过办公工具集成、会议能力强化、业务系统对接等…

C++课设:高效的日程管理系统

名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 专栏介绍&#xff1a;《编程项目实战》 目录 一、C日程管理系统的时代价值1. 为什么选…

功能测试、性能测试、安全测试详解

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、功能测试 1、单接口功能 手工测试中的单个业务模块&#xff0c;一般对应一个接口 例如&#xff1a; 登录业务------登录接口 加入购物车业务------加入购…

提示词指南 --- 提示词的基本结构

提示词指南 --- 提示词的基本结构以及三种角色 什么是Prompt (提示词)Prompt的基本结构和三种角色提示词的三种核心“角色”&#xff08;Role&#xff09; 真实例子 什么是Prompt (提示词) 我们可以把“Prompt&#xff08;提示词&#xff09;”想象成和AI聊天时你说的“一句话…

20250605使用boot-repair来恢复WIN10和ubuntu22.04.6双系统的启动

rootrootrootroot-X99-Turbo:~$ sudo apt-get install boot-repair rootrootrootroot-X99-Turbo:~$ sudo add-apt-repository ppa:yannubuntu/boot-repair rootrootrootroot-X99-Turbo:~$ sudo apt-get install boot-repair 20250605使用boot-repair来恢复WIN10和ubuntu22.04.6…

接口安全SOAPOpenAPIRESTful分类特征导入项目联动检测

1 、 API 分类特征 SOAP - WSDL OpenApi - Swagger RESTful - /v1/api/ 2 、 API 常见漏洞 OWASP API Security TOP 10 2023 3 、 API 检测流程 接口发现&#xff0c;遵循分类&#xff0c;依赖语言&#xff0c; V1/V2 多版本等 Method &#xff1a;请求方法 攻击方…

视频汇聚平台EasyCVR“明厨亮灶”方案筑牢旅游景区餐饮安全品质防线

一、背景分析​ 1&#xff09;政策监管刚性需求​&#xff1a;国家食品安全战略及 2024年《关于深化智慧城市发展的指导意见》要求构建智慧餐饮场景&#xff0c;推动数字化监管。多地将“AI明厨亮灶”纳入十四五规划考核&#xff0c;要求餐饮单位操作可视化并具备风险预警能力…

仓库自动化搬运:自动叉车与AGV选型要点及核心技术解析

自动叉车与AGV均可实现自主作业&#xff0c;无需人工驾驶即可搬运托盘化货物。然而&#xff0c;这两种解决方案存在一些关键差异。 自动叉车与AGV的对比 自动叉车与AGV是截然不同的车辆&#xff0c;其差异主要源于原始设计&#xff1a; 自动叉车是制造商对传统手动叉车进行改…

NLP学习路线图(二十五):注意力机制

在自然语言处理领域&#xff0c;序列模型一直扮演着核心角色。从早期的循环神经网络&#xff08;RNN&#xff09;到如今一统天下的Transformer模型&#xff0c;注意力机制&#xff08;Attention Mechanism&#xff09; 的引入堪称一场革命。它彻底改变了模型处理序列信息的方式…

05 APP 自动化- Appium 单点触控 多点触控

文章目录 一、单点触控查看指针的指针位置实现手势密码&#xff1a; 二、多点触控 一、单点触控 查看指针的指针位置 方便查看手势密码-九宫格每个点的坐标 实现手势密码&#xff1a; 执行手势操作&#xff1a; 按压起点 -> 移动到下一点 -> 依次移动 -> 释放&am…

[AI绘画]sd学习记录(一)软件安装以及文生图界面初识、提示词写法

目录 目录一、安装软件二、文生图各部分模块 1. 下载新模型 & 画出第一张图2. 提示词输入 2.1 设置2.2 扩展模型2.3 扩展模型权重调整2.4 其他提示词输入2.5 负向提示词2.6 生成参考 3. 采样方法4. 噪声调度器5. 迭代步数6. 提示词引导系数 一、安装软件 软件安装&…

SpringBoot(八) --- SpringBoot原理

目录 一、配置优先级 二、Bean的管理 1. Bean的作用域 2. 第三方Bean 三、SpringBoot原理 1. 起步依赖 2. 自动配置 3. 自动配置原理分析 3.1 源码解析 3.2 Conditional 一、配置优先级 SpringBoot项目当中支持三类配置文件&#xff1a; application.properties a…

C# 类和继承(抽象成员)

抽象成员 抽象成员是指设计为被覆写的函数成员。抽象成员有以下特征。 必须是一个函数成员。也就是说&#xff0c;字段和常量不能为抽象成员。必须用abstract修饰符标记。不能有实现代码块。抽象成员的代码用分号表示。 例如&#xff0c;下面取自一个类定义的代码声明了两个抽…

鸿蒙仓颉语言开发实战教程:商城登录页

听说Pura80要来了&#xff1f;感觉华为的新品像下饺子一样&#xff0c;让人目不暇接&#xff0c;每隔几天就有发布会看&#xff0c;真不错呀。 节后第一天&#xff0c;为了缓解大家假期的疲惫&#xff0c;咱们今天做点简单的内容&#xff0c;就是商城的登录页面。 其实这一次分…

JavaScript 数组与流程控制:从基础操作到实战应用

在 JavaScript 编程的世界里&#xff0c;数组是一种极为重要的数据结构&#xff0c;它就像是一个有序的 “收纳盒”&#xff0c;能够将多个值整齐地存储起来。而流程控制语句则像是 “指挥官”&#xff0c;能够按照特定的逻辑对数组进行遍历和操作。接下来&#xff0c;就让我们…

SkyWalking架构深度解析:分布式系统监控的利器

一、SkyWalking概述 SkyWalking是一款开源的APM(应用性能监控)系统&#xff0c;专门为微服务、云原生和容器化架构设计。它由Apache软件基金会孵化并毕业&#xff0c;已成为分布式系统监控领域的明星项目。 核心特性 ‌分布式追踪‌&#xff1a;跨服务调用链路的完整追踪‌服务…