【C语言选择排序算法详解】+ 算法性能优化 + 动态演示实现

文章目录

      • 一、算法介绍
      • 二、算法特点
      • 三、代码实现与解析
      • 四、代码解析
          • 1. 打印数组函数
          • 2. 选择排序核心逻辑
          • 3. 动态展示实现
          • 4. 主函数
      • 五、算法优化思路与实现
          • 优化1:减少交换次数
          • 优化原理:
          • 优化2:双向选择排序
          • 优化原理:
          • 优化3:提前终止检查
          • 优化原理:
      • 总结:

一、算法介绍

选择排序(Selection Sort)是一种简单直观的排序算法。它的工作原理是:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

二、算法特点

时间复杂度:O(n²)

空间复杂度:O(1)

不稳定排序算法

原地排序算法

三、代码实现与解析

#include <stdio.h>
#include <windows.h>  // 用于Sleep函数(Windows环境)// 打印数组
void loop_print_array(int arr[], int len) {for (int i = 0; i < len; i++) {printf("%d ", arr[i]);}printf("\n");
}// 选择排序(添加动态展示逻辑)
void selection_sort(int arr[], int len) {for (int i = 0; i < len - 1; i++) {for (int j = i + 1; j < len; j++) {if (arr[i] > arr[j]) {// 交换元素int temp = arr[i];arr[i] = arr[j];arr[j] = temp;// 🔴 关键:交换后打印当前数组 + 暂停system("cls");  // Windows下清屏,让显示更连贯(Linux/macOS用system("clear"))printf("第 %d 轮比较,交换 arr[%d] 和 arr[%d] 后:\n", i+1, i, j);loop_print_array(arr, len);Sleep(1000);  // 暂停800毫秒(可调整速度,单位:毫秒)}}}
}
int main() {// 设置控制台输出编码为UTF-8,避免中文乱码SetConsoleOutputCP(CP_UTF8);SetConsoleCP(CP_UTF8);int arr[10] = {5, 8, 1, 4, 2, 9, 10, 3, 7, 6};int len = sizeof(arr) / sizeof(int);printf("排序前:\n");loop_print_array(arr, len);Sleep(1000);  // 先暂停1秒,看清初始数组selection_sort(arr, len);printf("\n排序完成:\n");loop_print_array(arr, len);return 0;
}

四、代码解析

1. 打印数组函数

c
void loop_print_array(int arr[], int len) {
for (int i = 0; i < len; i++) {
printf(“%d “, arr[i]);
}
printf(”\n”);
}
这个函数负责遍历数组并打印所有元素,方便我们观察排序过程中数组的变化。

2. 选择排序核心逻辑
void selection_sort(int arr[], int len) {for (int i = 0; i < len - 1; i++) {for (int j = i + 1; j < len; j++) {if (arr[i] > arr[j]) {// 交换元素int temp = arr[i];arr[i] = arr[j];arr[j] = temp;// 动态展示部分system("cls");printf("第 %d 轮比较,交换 arr[%d] 和 arr[%d] 后:\n", i+1, i, j);loop_print_array(arr, len);Sleep(1000);}}}
}

选择排序的核心是双重循环:

  • 外层循环控制排序的轮数,每轮确定一个最小元素的位置

  • 内层循环用于查找未排序部分的最小元素

  • 当找到比当前基准更小的元素时,进行交换

3. 动态展示实现

代码中添加了动态展示逻辑:

使用 system(“cls”) 清屏,使每次输出更加清晰

打印当前操作信息(第几轮比较,交换了哪些元素)

使用 Sleep(1000) 暂停1秒,方便观察每一步的变化

4. 主函数
int main() {SetConsoleOutputCP(CP_UTF8);SetConsoleCP(CP_UTF8);int arr[10] = {5, 8, 1, 4, 2, 9, 10, 3, 7, 6};int len = sizeof(arr) / sizeof(int);// 初始数组展示printf("排序前:\n");loop_print_array(arr, len);Sleep(1000);// 执行排序selection_sort(arr, len);// 最终结果展示printf("\n排序完成:\n");loop_print_array(arr, len);return 0;
}

主函数中:

设置控制台编码为UTF-8,避免中文乱码

初始化待排序数组

展示排序前的数组

调用选择排序函数

展示排序后的最终结果

五、算法优化思路与实现

虽然选择排序的时间复杂度固定为O(n²),但仍可以做一些优化来提高实际性能:

优化1:减少交换次数

原始实现中每次找到更小的元素就立即交换,实际上我们可以记录最小值的索引,在一轮比较完成后只交换一次:

// 优化版本1:减少交换次数
void selection_sort_optimized(int arr[], int len) {for (int i = 0; i < len - 1; i++) {int min_index = i; // 记录最小元素的索引for (int j = i + 1; j < len; j++) {if (arr[j] < arr[min_index]) {min_index = j; // 更新最小元素的索引}}// 只有在找到更小元素时才交换if (min_index != i) {int temp = arr[i];arr[i] = arr[min_index];arr[min_index] = temp;// 动态展示system("cls");printf("第 %d 轮,找到最小值 arr[%d]=%d,与 arr[%d]=%d 交换后:\n", i+1, min_index, arr[i], i, arr[min_index]);loop_print_array(arr, len);Sleep(1000);}}
}
优化原理:

减少不必要的交换操作,每轮最多只交换一次

对于大型对象或复杂数据结构,交换操作可能成本较高,此优化能显著提高性能

优化2:双向选择排序

同时寻找最大值和最小值,减少排序轮数:

// 优化版本2:双向选择排序
void selection_sort_bidirectional(int arr[], int len) {int left = 0, right = len - 1;while (left < right) {int min_index = left, max_index = right;// 确保min_index <= max_indexif (arr[min_index] > arr[max_index]) {int temp = arr[min_index];arr[min_index] = arr[max_index];arr[max_index] = temp;}// 在剩余部分中查找最小和最大值for (int i = left + 1; i < right; i++) {if (arr[i] < arr[min_index]) {min_index = i;} else if (arr[i] > arr[max_index]) {max_index = i;}}// 将最小值放到左边if (min_index != left) {int temp = arr[left];arr[left] = arr[min_index];arr[min_index] = temp;// 如果最大值原本在left位置,现在被移到了min_index位置if (max_index == left) {max_index = min_index;}}// 将最大值放到右边if (max_index != right) {int temp = arr[right];arr[right] = arr[max_index];arr[max_index] = temp;}// 动态展示system("cls");printf("范围 [%d,%d],最小值放左边,最大值放右边后:\n", left, right);loop_print_array(arr, len);Sleep(1000);left++;right--;}
}
优化原理:

每轮同时找到最小和最大元素,分别放到序列的两端

减少大约一半的排序轮数

对于随机数据,性能提升约50%

优化3:提前终止检查

添加提前终止检查,当数组已经有序时提前结束排序:

// 优化版本3:添加提前终止检查
void selection_sort_early_termination(int arr[], int len) {for (int i = 0; i < len - 1; i++) {int min_index = i;bool swapped = false; // 标记本轮是否发生交换for (int j = i + 1; j < len; j++) {if (arr[j] < arr[min_index]) {min_index = j;swapped = true;}}if (min_index != i) {int temp = arr[i];arr[i] = arr[min_index];arr[min_index] = temp;// 动态展示system("cls");printf("第 %d 轮,交换 arr[%d] 和 arr[%d] 后:\n", i+1, i, min_index);loop_print_array(arr, len);Sleep(1000);} else if (!swapped) {// 如果没有找到更小的元素且未发生交换,说明数组已有序printf("第 %d 轮检测到数组已有序,提前终止排序\n", i+1);break;}}
}
优化原理:

当某一轮未发生任何交换时,说明数组已经有序,可以提前终止排序

对于近乎有序的数组,能显著提高性能

性能对比
为了直观展示优化效果,我们可以添加简单的性能测试代码:

#include <time.h>// 性能测试函数
void performance_test() {const int SIZE = 10000;int arr1[SIZE], arr2[SIZE], arr3[SIZE];// 初始化随机数组for (int i = 0; i < SIZE; i++) {int val = rand() % 1000;arr1[i] = val;arr2[i] = val;arr3[i] = val;}clock_t start, end;// 测试原始版本start = clock();selection_sort(arr1, SIZE);end = clock();printf("原始版本耗时: %.3f 秒\n", (double)(end - start) / CLOCKS_PER_SEC);// 测试优化版本1start = clock();selection_sort_optimized(arr2, SIZE);end = clock();printf("优化版本1耗时: %.3f 秒\n", (double)(end - start) / CLOCKS_PER_SEC);// 测试优化版本2start = clock();selection_sort_bidirectional(arr3, SIZE);end = clock();printf("优化版本2耗时: %.3f 秒\n", (double)(end - start) / CLOCKS_PER_SEC);
}

运行效果
运行程序后,控制台会逐步展示排序过程:

首先显示初始数组:5 8 1 4 2 9 10 3 7 6

每一轮比较后,清屏并显示当前数组状态和操作信息

最后显示排序完成的有序数组:1 2 3 4 5 6 7 8 9 10

适用场景与总结
选择排序是一种简单但低效的排序算法,主要适用于:

  1. 小规模数据排序

  2. 对内存使用有严格限制的场景

  3. 作为算法学习的入门示例

总结:

选择排序的时间复杂度为O(n²),不适合大规模数据排序

通过优化可以减少交换次数和排序轮数,提高实际性能

对于大规模数据排序,建议使用更高效的算法如快速排序、归并排序等。

Created by LiuJinTao on 2025/9/13.

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

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

相关文章

栈(Java)

提示&#xff1a;多练才是王道,加油٩(๑❛ᴗ❛๑)۶ 栈Java1. 栈2. Java中栈的其中两种实现方式2.1 Stack类2.1.1 Stack的模拟实现2.2 LinkedList类3. 典型习题讲解3.1 逆波兰表达式求值3.2 匹配括号3.3 合理弹出序列3.4 最小栈1. 栈 栈是一种特殊的线性表,其只允许在固定的一…

LayaAir鼠标(手指)控制相机旋转,限制角度

切换天空盒脚本挂载到相机身上 const { regClass, property } Laya;regClass() export class SmoothCameraController extends Laya.Script {declare owner: Laya.Camera;// 旋转灵敏度property({ type: Number, name: "旋转灵敏度" })public rotationSensitivity:…

【数据结构入门】排序算法(4)归并排序

目录 1.排序的原理 1.1 保证子数组有序 1.2 时间复杂度 2. 递归实现 2.1 思路 2.2 代码 3. 非递归实现 3.1 思路 3.2 代码 4.面试题 4.1 题目 4.2 思路 1.排序的原理 归并排序是外排序&#xff0c;所谓外排序就是说能够对文件中的数据进行排序。 ①首先&#xff…

FLEXSPI_Init 硬件故障问题

使用官方例程发现FLEXSPI_Init会引起硬件故障&#xff0c;查阅相关帖子发现主要有两个可能&#xff1a;1、外部闪存配置差异修改 LUT&#xff08;查找表&#xff09;命令&#xff1a;示例中擦除扇区命令为 0xD7&#xff0c;写状态寄存器命令为 0x01&#xff0c;需分别改为 闪存…

如何用 Rust 重写 SQLite 数据库(一):项目探索

要使用 Rust 重写 SQLite 数据库&#xff0c;我们需要实现一个简化的关系型数据库核心功能&#xff08;如 SQL 解析、存储引擎、事务管理&#xff09;。以下是一个分步实践指南&#xff0c;包含关键代码示例。一、项目规划 我们将实现一个超简化数据库 MiniSQL&#xff0c;支持…

JVM之堆(Heap)

一、堆的核心特性 唯一性与共享性 每个JVM实例仅有一个堆&#xff0c;所有线程共享&#xff0c;但可通过线程私有缓冲区&#xff08;TLAB&#xff09;减少多线程分配冲突。内存结构演变 JDK 7及之前&#xff1a;堆分为新生代&#xff08;Young&#xff09;、老年代&#xff08;…

单片机的RAM与ROM概念

RAM与ROM1、RAM与ROM2、 bss、data、heap、stack、text详细讲解3、详细探讨 TCM、OCRAM 和 HBNRAM 之间的区别及其具体作用。3.1、TCM&#xff08;Tightly Coupled Memory&#xff09;3.2、 OCRAM&#xff08;On Chip RAM&#xff09;3.3、HBNRAM (Hibernate RAM)3.4、总结1、R…

实验3:事件处理(2学时)

实验目的&#xff08;1&#xff09;熟练掌握 v-on 指令的用法&#xff0c;学会使用 v-on 指令监听 DOM 元素的事件&#xff0c;并通过该事件触发调用事件处理程序。&#xff08;2&#xff09;掌握v-on 指令修饰符的基本用法。实验内容实现购物车功能的拓展&#xff08;商品数量…

商品库存扣减方案

文章目录1. Lua脚本 Redis&#xff08;业界首选&#xff0c;综合最优&#xff09;2. Redis原子命令&#xff08;DECRBY 结果校验&#xff09;3. Redis事务&#xff08;MULTI/EXEC&#xff09;4. 分布式锁&#xff08;基于Redis实现&#xff09;5. Redisson客户端封装&#xf…

关于在阿里云DMS误操作后如何恢复数据的记录

前言 昨天因客户员工操作错误&#xff0c;导致快递单号和订单互换。客户员工那边让笔记修改数据。 于是笔者写下如下SQL来操作&#xff0c;导致了灾难性事故。 update t_order_fed_ex_record set tracking_number 884102170661, master_tracking_number 884102170661, push…

【操作系统核心知识梳理】线程(Thread)重点与易错点全面总结

在多任务操作系统中&#xff0c;线程是比进程更轻量的执行单元&#xff0c;理解线程的特性和实现方式是掌握并发编程的基础。本文系统梳理了线程相关的核心知识点和常见误区&#xff0c;助你夯实操作系统基础。一、线程的基本概念与引入目的 1.1 什么是线程&#xff1f; 线程是…

深入理解 Python 中的 `__call__` 方法

化身为可调用的对象&#xff1a;深入理解 Python 中的 __call__ 方法 引言&#xff1a;函数与对象的边界模糊化 在 Python 中&#xff0c;我们最熟悉的概念莫过于函数&#xff08;Function&#xff09; 和对象&#xff08;Object&#xff09;。函数是可调用的&#xff08;calla…

云服务器使用代理稳定与github通信方法

使用SSH反向隧道 (SSH Reverse Tunneling) 利用SSH连接在您的本地电脑和云服务器之间建立一个反向的加密通道。 原理&#xff1a; 从本地电脑发起一个SSH命令到您的云服务器&#xff0c;这个命令会告诉云服务器&#xff1a;“请监听您自己的某个端口&#xff08;例如&#xff1…

7.k8s四层代理service

Service的基本介绍 Cluster IP&#xff1a;每个 Service 都分配了一个Cluster IP&#xff0c;它是一个虚拟的内部IP地址&#xff0c;用于在集群内部进行访问。这个虚拟IP是由Kubernetes自动分配的&#xff0c;并且与Service对象一一对应。 端口映射&#xff1a;Service可以映射…

Qt 工程中 UI 文件在 Makefile 中的处理

Qt 工程中 UI 文件在 Makefile 中的处理 在 Qt 工程中&#xff0c;.ui 文件&#xff08;Qt Designer 界面文件&#xff09;需要通过 uic&#xff08;用户界面编译器&#xff09;工具转换为对应的头文件。以下是几种情况下如何处理 UI 文件&#xff1a;1. 使用 qmake 自动生成 M…

ZLMediaKit性能测试

一、环境 系统&#xff1a;虚拟机 Ubuntu22.04 64bit配置: 4核8G设置&#xff1a;ulimit -n 102400 二、安装 依赖安装sudo apt update sudo apt install ffmpeg sudo apt install nloadzlm服务安装参考&#xff1a;https://blog.csdn.net/hanbo622/article/details/149064939?…

智能文档处理业务,应该选择大模型还是OCR专用小模型?

智能文档处理业务中&#xff0c;最佳策略不是二选一&#xff0c;而是“大小模型协同”。用专用小模型处理高频、标准化的核心文档流&#xff0c;实现极致效率与成本控制&#xff1b;用大模型赋能非标、长尾文档的灵活处理&#xff0c;加速业务创新。 OCR小模型会被大模型取代吗…

android 如何判定底部导航栏显示时 不是键盘显示

在 Android 中判定底部导航栏是否显示时&#xff0c;核心痛点是 区分 “导航栏的底部 Insets” 和 “软键盘弹出的底部 Insets”—— 两者都会导致 getSystemWindowInsetBottom() 返回非零值&#xff0c;直接判断会误将键盘弹出当成导航栏显示。以下是基于 WindowInsets 类型区…

你知道服务器和电脑主机的区别吗?

我们都知道服务器和台式主机有着不同之处&#xff0c;但具体说出个一二三来很多人还是一头雾水&#xff0c;也就是知其然不知其所以然&#xff0c;都是CPU主板 内存 硬盘 电源&#xff0c;撑死就差一个显卡不同&#xff0c;但其实服务器和我们正常使用的台式主机差距很大&#…

什么是包装类

什么是包装类 在Java中&#xff0c;包装类&#xff08;Wrapper Class&#xff09;是为基本数据类型提供的对应的引用类型。Java中的基本数据类型&#xff08;如int、char、boolean等&#xff09;不是对象&#xff0c;为了在需要对象的场景中使用基本数据类型&#xff08;如集合…