五种排序算法(C语言实现)

五种经典排序算法

文章目录

冒泡排序

  1. 代码实现

总体思路
通过对待排序序列从前向后(从下标较小的元素开始),依次对相邻两个元素的值进行两两比较,若发现前一个数大于后一个数则交换,使值较大的元素逐渐从前移向后部,就如同水底下的气泡一样逐渐向上冒
示意图:

img
#include<stdio.h>voidBubble_Sort(int arr[],int n){for(int i =0; i < n -1; i++)//外层循环控制轮数,即遍历数组的次数,每一轮会确定一个最大值放到末尾{for(int j =0; j < n - i -1; j++)//内层循环控制每一轮比较相邻两元素的次数{if(arr[j]> arr[j +1])//如果前一个比后一个大,交换{int temp = arr[j]; arr[j]= arr[j +1]; arr[j +1]= temp;}}}}intmain(){int arr[5];int n =sizeof(arr)/sizeof(arr[0]);for(int i =0; i < n; i++){scanf("%d",&arr[i]);}Bubble_Sort(arr, n);for(int j =0; j < n; j++){printf("%d ", arr[j]);}return0;}
  1. 关键点解释
  • 先介绍在这个程序中两个重要的概念:轮数和比较次数。轮数是遍历数组的次数,每一次遍历都会确定一个最大数冒泡到最后;次数是每一轮要比较相邻两两元素多少次
  • n代表元素个数
  • 在函数Bubble_Sort的嵌套循环中,外层循环控制轮数i,(i = n-1),内层循环控制比较次数j,(j=n-i-1)

插入排序

  1. 总体思路
    插入排序的核心思想是将未排序的元素逐个插入已排序部分的合适位置,直到整个数组排序完成
    示意图:
在这里插入图片描述
  1. 代码实现
#include<stdio.h>voidInsert_Sort(int arr[],int n){int key,i,j;// 将第一个元素视为已排列部分,从第二个元素开始遍历数组,将其插入到已排序部分的合适位置for(i =1; i < n; i++){ key = arr[i];//key是当前要插入的元素int j = i -1;//循环之前,arr[j]是已排序部分的最后一个元素while(j >=0&& arr[j]> key){ arr[j +1]= arr[j];// 将大于key的元素向后移动 j--;}// 循环j结束之后,arr[j]此时刚好是不大于key的数,将key插入到arr[j]之后一个位置即arr[j+1] arr[j+1]= key;}}// 测试插入排序的例子intmain(){int arr[5];int n =sizeof(arr)/sizeof(arr[0]);for(int i =0; i < n; i++){scanf("%d",&arr[i]);}Insert_Sort(arr, n);for(int i =0; i < n; i++){printf("%d ", arr[i]);}return0;}
  1. 关键点解释
  • 初始已排序部分:将第一个元素视为已排序部分
  • 逐个插入:遍历未排序部分的每个元素,在已排序部分中找到合适的位置并插入
  • 重复直到完成:重复以上步骤,直到所有元素都插入到已排序部分

选择排序

  1. 总体思路
    选择排序是通过遍历数组,选择出数组的最小值,与指定位置交换数据,遍历完整个数组的所有位置就完成排序
    示意图:
在这里插入图片描述
  1. 代码实现
#include<stdio.h>voidSelect_Sort(int arr[],int n){int i, j, min_index;//i是未排序部分的第一个元素的索引,j是i之后一个元素的索引,min_index是未排序部分最小元素的索引for(i =0; i < n -1; i++)//外层循环遍历数组的前n-1个元素{ min_index = i;for(j = i +1; j < n; j++)//内层循环用来遍历未排序部分第一个元素之后的所有元素,找到其中的最小值与未排序部分第一个元素进行交换{if(arr[min_index]> arr[j]){ min_index = j;}}int temp = arr[i];//交换 arr[i]= arr[min_index]; arr[min_index]= temp;}}//主函数测试intmain(){int arr[5];int n =sizeof(arr)/sizeof(arr[0]);for(int i =0; i < n; i++){scanf("%d",&arr[i]);}Select_Sort(arr, n);for(int j =0; j < n; j++){printf("%d ", arr[j]);}return0;}
  1. 关键点解释
  • 本方法的核心是每次将数组未排序部分第一个元素之后的若干个数字中的最小值和未排序部分的第一个元素交换
  • i是未排序部分第一个元素的索引,j是i后面一个元素的索引,min_index是未排序部分最小元素的索引

快速排序

  1. 总体思路
  • 概念
    快速排序是一种高效的排序算法,采用分治策略。它的基本思想是:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,然后分别对这两部分记录继续进行排序,以达到整个序列有序
  • 总体步骤
    • 选择基准值:从数列中挑出一个元素作为"基准"(一般挑选数组第一个元素或最后一个元素)
    • 分区操作:将数列重新排列,所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基准后面

递归排序:递归地对基准值左右两个子序列进行快速排序
示意图:

img
  1. 代码实现
#include<stdio.h>// 交换两个元素的值voidSwap(int* a,int* b){int temp =*a;*a =*b;*b = temp;}// Hoare法分区函数实现单趟排序intPartSort(int* arr,int left,int right){int key = left;// 使用key保存基准值下标while(left < right){while(left < right && arr[key]<= arr[right]){// 只找小的,等于要过滤,找前判断right有没有走过 right--;}while(left < right && arr[key]>= arr[left]){ left++;}if(left < right){// 没有相遇时左右交换Swap(&arr[left],&arr[right]);}}// left和right相遇后交换基准值和相遇位置的值,并返回基准值的下标Swap(&arr[key],&arr[left]);return left;}// 快速排序递归实现voidQuickSort(int* arr,int begin,int end){// 当区间不存在或只有一个元素时,不需要排序,直接返回if(begin >= end){return;}// 单趟排序,获取基准值位置int keyIndex =PartSort(arr, begin, end);// 递归排序左半部分 [begin, keyIndex-1]QuickSort(arr, begin, keyIndex -1);// 递归排序右半部分 [keyIndex+1, end]QuickSort(arr, keyIndex +1, end);}// 测试代码intmain(){int arr[]={6,1,2,7,9,3,4,5,10,8};int n =sizeof(arr)/sizeof(arr[0]);printf("排序前: ");for(int i =0; i < n; i++){printf("%d ", arr[i]);}printf("\n");QuickSort(arr,0, n -1);printf("排序后: ");for(int i =0; i < n; i++){printf("%d ", arr[i]);}printf("\n");return0;}
  1. 关键点解释
  • PartSort函数思路:
    • 初始化:选择arr[left]作为基准值,key记录基准值位置
    • 右指针移动:从右向左扫描,找到第一个小于基准值的元素
    • 左指针移动:从左向右扫描,找到第一个大于基准值的元素
    • 元素交换:交换左右指针所指的元素
    • 重复过程:继续移动指针并交换,直到左右指针相遇
    • 基准归位:将基准值交换到相遇位置
  • QuickSort函数思路分析:
    • 分解:通过PartSort将数组分成两个子数组。左子数组:所有元素 ≤ 基准值;右子数组:所有元素 ≥ 基准值
    • 解决:递归地排序两个子数组
      QuickSort(arr, begin, keyIndex - 1):排序左半部分
      QuickSort(arr, keyIndex + 1, end):排序右半部分
    • 合并:由于是原地排序,不需要显式合并步骤

归并排序

  1. 总体思路
    归并排序采用分治策略:
    :将数组递归地分成两半,直到每个子数组只有一个元素(这时候认为每一个单元素数组为有序数组)
    :将两个已排序的子数组合并成一个有序数组,直到所有元素排序完成

一张示意图帮助理解这个过程

在这里插入图片描述
  1. 代码实现
#include<stdio.h>#include<stdlib.h>//merge函数:合并两个有序数组voidmerge(int arr[],int left,int mid,int right){// left: 左子数组起始索引, mid: 中间点, right: 右子数组结束索引int n1 = mid - left +1;// 计算左半部分元素个数int n2 = right - mid;// 计算右半部分元素个数//边界检查if(left > right || mid < left || mid >= right){return;}// 检查数组大小是否有效if(n1 <=0|| n2 <=0){return;}// 创建临时数组存放左右两半的数据int* L =(int*)malloc(n1 *sizeof(int));int* R =(int*)malloc(n2 *sizeof(int));//内存分配检查if(L ==NULL|| R ==NULL){if(L)free(L);if(R)free(R);printf("内存分配失败\n");return;}// 拷贝数据:把原数组分成左右两个临时数组for(int i =0; i < n1; i++){ L[i]= arr[left + i];// 拷贝左半部分}for(int j =0; j < n2; j++){ R[j]= arr[mid +1+ j];// 拷贝右半部分}// 关键步骤:合并两个有序数组int i =0, j =0, k = left;// i指向L, j指向R, k指向原数组// 比较两个数组的元素,取较小的放入原数组while(i < n1 && j < n2){if(L[i]<= R[j]){// 稳定排序的关键:相等时取左边的 arr[k]= L[i];// 左数组元素较小,放入原数组 i++;// 左数组指针后移}else{ arr[k]= R[j];// 右数组元素较小,放入原数组 j++;// 右数组指针后移} k++;// 原数组指针后移}// 处理剩余元素:如果左数组还有剩余,全部拷贝过去while(i < n1){ arr[k]= L[i]; i++; k++;}// 处理剩余元素:如果右数组还有剩余,全部拷贝过去while(j < n2){ arr[k]= R[j]; j++; k++;}free(L);// 释放临时数组内存free(R);}//mergeSort函数:归并排序主函数voidmergeSort(int arr[],int left,int right){if(left < right){// 递归终止条件:子数组至少要有2个元素int mid = left +(right - left)/2;// 防止整数溢出的取中方法if(mid >= left && mid < right){mergeSort(arr, left, mid);// 递归排序左半部分mergeSort(arr, mid +1, right);// 递归排序右半部分merge(arr, left, mid, right);// 合并两个已排序的部分}}}//main函数测试intmain(){int arr[]={12,11,13,5,6,7};int n =sizeof(arr)/sizeof(arr[0]);printf("原始数组: ");for(int i =0; i < n; i++){printf("%d ", arr[i]);}printf("\n");mergeSort(arr,0, n -1);printf("归并排序后: ");for(int i =0; i < n; i++){printf("%d ", arr[i]);}printf("\n");return0;}
  1. 关键点解释
  • 关键思想
    • 分治策略:将大问题分解为小问题解决,再合并结果
    • 递归实现:不断将数组二分,直到单个元素自然有序
  • 关键步骤
    分解阶段
    • 计算中间点:mid = left + (right - left) / 2(防溢出)
    • 递归分解左右子数组,直到单个元素
      合并阶段(核心)
    • 创建两个临时数组存放左右子数组
    • 双指针比较:比较两个子数组当前元素,取较小者放入原数组
    • 稳定性保证:L[i] <= R[j]确保相等时左数组元素优先
    • 剩余元素处理
    • 某个子数组有剩余时,直接全部拷贝(因为已有序)
      形象比喻
      就像把一副乱牌分成两半,分别整理好后再像发牌一样合并

总结

  1. 冒泡排序: 重复遍历,两两比较,大的下沉。像气泡一样,每一轮将最大的元素“浮”到最终位置
  2. 选择排序: 打擂台,选最小,放前面。每一轮从未排序部分中选出最小(或最大)的元素,将其与未排序部分的第一个元素交换
  3. 插入排序: 摸牌理牌,逐个插入。像打扑克摸牌一样,将每个新元素插入到前面已经排好序的序列中的正确位置
  4. 快速排序: 选定基准,小数左大数右,递归处理。选择一个“基准”元素,将数组分成“小于基准”和“大于基准”的两部分,然后对这两部分递归地进行相同操作
  5. 归并排序: 先分后合,有序合并。采用分治法,先将数组递归地分成两半,直到不可再分,然后再将两个有序的子数组合并成一个更大的有序数组

Read more

Flutter 组件 flutterw_sidekick_plugin 适配鸿蒙 HarmonyOS 实战:侧翼脚手架扩展,构建工程自动化与环境一致性治理架构

Flutter 组件 flutterw_sidekick_plugin 适配鸿蒙 HarmonyOS 实战:侧翼脚手架扩展,构建工程自动化与环境一致性治理架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 flutterw_sidekick_plugin 适配鸿蒙 HarmonyOS 实战:侧翼脚手架扩展,构建工程自动化与环境一致性治理架构 前言 在鸿蒙(OpenHarmony)生态迈向大规模团队协作、涉及多分支并行开发及复杂的 SDK 版本管控的背景下,如何确保每一位开发者的本地构建环境(Flutter/Dart SDK)与生产基准完全对齐,已成为保障项目交付质量的“工程定海神针”。在鸿蒙设备这类强调定制化编译工具链与私有插件依赖的环境下,如果团队缺乏统一的脚手架工具,由于由于本地 SDK 版本的微小代差(如空安全检测差异),极易由于由于“环境不一致”导致代码在不同机器上产生不可预知的编译崩溃。 我们需要一种能够深度集成 Sidekick、支持自定义命令扩展且具备“强制版本锁死”能力的脚手架治理方案。 flutterw_sidekick_plugin 为 Flutter 开发者引入了基于 Sidekick

By Ne0inhk
Flutter 组件 freezed_collection 的鸿蒙化适配实战 - 驾驭极致集合不可变性大坝、构建 OpenHarmony 分布式端高性能、防篡改、类型安全的数据阵列方案

Flutter 组件 freezed_collection 的鸿蒙化适配实战 - 驾驭极致集合不可变性大坝、构建 OpenHarmony 分布式端高性能、防篡改、类型安全的数据阵列方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 freezed_collection 的鸿蒙化适配实战 - 驾驭极致集合不可变性大坝、构建 OpenHarmony 分布式端高性能、防篡改、类型安全的数据阵列方案 前言 在鸿蒙(OpenHarmony)生态的工业级交付、重型金融结算以及对业务逻辑零缺陷容忍的跨端政务系统中。“集合数据的不可变性与深层防篡改维度”是衡量整个系统架构鲁棒性的最终质量门禁。面对包含数万个 SKU 商品详情、海量设备状态快照、甚至是金融流水大波次的 0308 批次工程大盘。如果仅仅依靠 Dart 原生的 List.unmodifiable 或者是干瘪的运行时报错。不仅会导致在定位多线程并发竞态(Race Condition)时让架构师如同在逻辑废墟中盲人摸象。更会因为缺乏编译期强制约束。令整个系统的状态管理在跨设备同步时陷入严重的混乱盲区。 我们需要一种“逻辑严丝合缝、操作物理隔离”的集合资产保护艺术。 freezed_collection 是一套专注于无缝整

By Ne0inhk
【MySQL】win 10 / win11:mysql 5.7 下载、安装与配置

【MySQL】win 10 / win11:mysql 5.7 下载、安装与配置

目录 一、MySQL 下载 (1)MySQL 官网下载地址 (2)下载保存安装包 二、MySQL 安装 (1)运行安装包 (2)选择安装类型 (3)选择安装版本号 (4)配置服务端口 (5)配置 root 的密码 (6)配置服务名称 (7)安装完成 三、配置系统环境变量 (1)打开系统环境变量配置面板 (2)编辑系统变量 Path 四、验证安装完成 五、Navicat 测试连接 (1)连接数据库 (2)填写连接信息 (3)测试连接 (4)保存连接 (5)高级配置(

By Ne0inhk
【MYSQL】MYSQL学习的一大重点:MYSQL表的操作

【MYSQL】MYSQL学习的一大重点:MYSQL表的操作

🎬 个人主页:艾莉丝努力练剑 ❄专栏传送门:《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》 《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》 ⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平 🎬 艾莉丝的简介: 文章目录 * 0 ~> 概要 * 1 ~> 创建表 * 2 ~> 创建表的案例详解 * 3 ~> 查看表结构 * 4 ~> 修改表 * 4.1 什么时候需要修改表 * 4.2 修改方式 * 4.3 案例 * 4.3.1 在users表添加二条记录 * 4.

By Ne0inhk