当前位置: 首页 > news >正文

算法 学习 排序 2025年6月16日10:25:37

比较排序 

简单排序 

  • 冒泡排序

重点:相邻元素两两比较,大的往后移动
使用场景:教学使用,实际应用较少(效率低)
时间复杂度:O(n²)(最坏和平均),O(n)(最好,已排序时)

void bubbleSort(int arr[], int n) {for (int i = 0; i < n-1; i++) {       // 外层循环控制轮数int swapped = 0;                  // 优化:标记是否发生交换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;swapped = 1;}}if (!swapped) break; // 本轮无交换,说明已有序}
}
冒泡排序调用示例(直观展示)
int arr[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(arr)/sizeof(arr[0]);printf("原始数组: ");
printArray(arr, n); // 输出: 64 34 25 12 22 11 90bubbleSort(arr, n);printf("排序后数组: ");
printArray(arr, n); // 输出: 11 12 22 25 34 64 90//流程变化
第1轮: 34 25 12 22 11 64 90 (最大的90已到位)
第2轮: 25 12 22 11 34 64 90 
第3轮: 12 22 11 25 34 64 90
...

  • 选择排序

重点:每次选择最小元素放到已排序序列末尾
使用场景:小规模数据或内存受限环境
时间复杂度:O(n²)

void selectionSort(int arr[], int n) {for (int i = 0; i < n-1; i++) {int min_idx = i; // 记录最小元素位置for (int j = i+1; j < n; j++) {if (arr[j] < arr[min_idx]) {min_idx = j; // 更新最小元素位置}}// 将最小元素交换到已排序序列末尾int temp = arr[i];arr[i] = arr[min_idx];arr[min_idx] = temp;}
}
选择排序调用示例(直观展示)
  int arr[] = {64, 25, 12, 22, 11};int n = sizeof(arr)/sizeof(arr[0]);printf("原始数组: ");for (int i = 0; i < n; i++) printf("%d ", arr[i]);printf("\n\n");selectionSort(arr, n);printf("\n最终结果: ");for (int i = 0; i < n; i++) printf("%d ", arr[i]);//流程
原始数组: 64 25 12 22 11 第1轮选择: [11] 25 12 22 64  // 最小11交换到首位
第2轮选择: 11 [12] 25 22 64  // 最小12交换到第2位
第3轮选择: 11 12 [22] 25 64  // 最小22交换到第3位
第4轮选择: 11 12 22 [25] 64  // 最小25交换到第4位最终结果: 11 12 22 25 64

  • 插入排序

重点:将未排序元素插入到已排序序列的合适位置
使用场景:小规模或基本有序数据
时间复杂度:O(n²)(最坏和平均),O(n)(最好)

void insertionSort(int arr[], int n) {for (int i = 1; i < n; i++) {  // 从第二个元素开始int key = arr[i];          // 当前待插入元素int j = i - 1;// 将大于key的元素后移while (j >= 0 && arr[j] > key) {arr[j+1] = arr[j];j--;}arr[j+1] = key; // 插入到正确位置}
}
插入排序调用示例(直观展示)
  int arr[] = {12, 11, 13, 5, 6};int n = sizeof(arr)/sizeof(arr[0]);printf("原始数组: ");for (int i = 0; i < n; i++) printf("%d ", arr[i]);printf("\n\n");insertionSort(arr, n);printf("\n最终结果: ");for (int i = 0; i < n; i++) printf("%d ", arr[i]);//流程
原始数组: 12 11 13 5 6 第1轮插入: |11| 12 13 5 6  // 11插入到12前
第2轮插入: 11 12 |13| 5 6  // 13保持位置
第3轮插入: |5| 11 12 13 6  // 5插入到最前
第4轮插入: 5 |6| 11 12 13  // 6插入到5和11之间最终结果: 5 6 11 12 13

进阶排序

  • 归并排序

重点:分治法,先分后合
使用场景:需要稳定排序且不关心额外空间时
时间复杂度:O(nlogn)

// 合并两个有序数组
void merge(int arr[], int l, int m, int r) {int n1 = m - l + 1;int n2 = r - m;// 创建临时数组int L[n1], R[n2];// 拷贝数据到临时数组for (int i = 0; i < n1; i++)L[i] = arr[l + i];for (int j = 0; j < n2; j++)R[j] = arr[m + 1 + j];// 合并临时数组int i = 0, j = 0, k = l;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++;}
}void mergeSort(int arr[], int l, int r) {if (l < r) {int m = l + (r - l) / 2; // 防止溢出mergeSort(arr, l, m);     // 排序左半部分mergeSort(arr, m+1, r);   // 排序右半部分merge(arr, l, m, r);      // 合并}
}
归并排序调用示例(直观展示)
int arr[] = {38, 27, 43, 3, 9, 82, 10};
int n = sizeof(arr)/sizeof(arr[0]);printf("原始数组: ");
printArray(arr, n); // 输出: 38 27 43 3 9 82 10mergeSort(arr, 0, n-1);printf("排序后数组: ");
printArray(arr, n); // 输出: 3 9 10 27 38 43 82//合并过程
分割到最小单元: [38] [27] → 合并为[27,38]
[43] [3] → 合并为[3,43]
[27,38]和[3,43] → 合并为[3,27,38,43]

  • 快速排序

重点:分治法,选取基准值分区
使用场景:大规模数据排序,性能要求高
时间复杂度:O(nlogn)(平均),O(n²)(最坏)

// 分区函数
int partition(int arr[], int low, int high) {int pivot = arr[high]; // 选取最后一个元素作为基准int i = (low - 1);     // 小于基准的元素的索引for (int j = low; j <= high-1; j++) {if (arr[j] < pivot) { // 当前元素小于基准i++;// 交换arr[i]和arr[j]int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}// 将基准放到正确位置int temp = arr[i+1];arr[i+1] = arr[high];arr[high] = temp;return (i + 1);
}void quickSort(int arr[], int low, int high) {if (low < high) {int pi = partition(arr, low, high); // 分区索引quickSort(arr, low, pi - 1);  // 排序左子数组quickSort(arr, pi + 1, high); // 排序右子数组}
}
快速排序调用示例(直观展示)
int arr[] = {10, 7, 8, 9, 1, 5};
int n = sizeof(arr)/sizeof(arr[0]);printf("原始数组: ");
printArray(arr, n); // 输出: 10 7 8 9 1 5quickSort(arr, 0, n-1);printf("排序后数组: ");
printArray(arr, n); // 输出: 1 5 7 8 9 10//分区过程
基准选择5: [1] 5 [10,7,8,9]  // 第一次分区结果
左子数组[10,7,8,9]选基准9: [7,8] 9 [10]  
继续处理[7,8]...

  • 希尔排序

重点:改进的插入排序,按增量分组
使用场景:中等规模数据,需要比O(n²)更好的性能
时间复杂度:O(n^(3/2))(取决于增量序列

void shellSort(int arr[], int n) {// 初始增量gap为数组长度的一半,逐步缩小for (int gap = n/2; gap > 0; gap /= 2) {// 对每个子数组进行插入排序for (int i = gap; i < n; i++) {int temp = arr[i];int j;for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {arr[j] = arr[j - gap];}arr[j] = temp;}}
}
 希尔排序调用示例(直观展示)
int arr[] = {35, 33, 42, 10, 14, 19, 27, 44};int n = sizeof(arr)/sizeof(arr[0]);printf("原始数组: ");for (int i = 0; i < n; i++) printf("%d ", arr[i]);printf("\n\n");shellSort(arr, n);printf("\n最终结果: ");for (int i = 0; i < n; i++) printf("%d ", arr[i]);//流程
原始数组: 35 33 42 10 14 19 27 44 增量gap=4时: 14 19 27 10 35 33 42 44  // 分组[35,14], [33,19], [42,27], [10,44]排序
增量gap=2时: 14 10 27 19 35 33 42 44  // 分组[14,27,35,42], [10,19,33,44]排序
增量gap=1时: 10 14 19 27 33 35 42 44  // 标准插入排序最终结果: 10 14 19 27 33 35 42 44

非比较排序

  • 基数排序

重点:按位数从低到高分别排序
使用场景:整数或字符串排序,位数不多
时间复杂度:O(nk)(k是最大数字的位数)

// 获取数组中最大数的位数
int getMaxDigits(int arr[], int n) {int max = arr[0];for (int i = 1; i < n; i++) {if (arr[i] > max) {max = arr[i];}}int digits = 0;while (max != 0) {digits++;max /= 10;}return digits;
}// 基数排序
void radixSort(int arr[], int n) {int max_digits = getMaxDigits(arr, n);int exp = 1; // 当前位数(1表示个位,10表示十位...)for (int d = 0; d < max_digits; d++) {// 10个桶(0-9)int buckets[10][n];int bucket_sizes[10] = {0};// 分配到桶中for (int i = 0; i < n; i++) {int digit = (arr[i] / exp) % 10;buckets[digit][bucket_sizes[digit]++] = arr[i];}// 从桶中收集回数组int index = 0;for (int i = 0; i < 10; i++) {for (int j = 0; j < bucket_sizes[i]; j++) {arr[index++] = buckets[i][j];}}exp *= 10; // 处理下一位}
}
基数排序 调用示例(直观展示)
int arr[] = {170, 45, 75, 90, 802, 24, 2, 66};
int n = sizeof(arr)/sizeof(arr[0]);printf("原始数组: ");
printArray(arr, n); radixSort(arr, n);printf("排序后数组: ");
printArray(arr, n); // 输出: 2 24 45 66 75 90 170 802//流程
个位排序: 170 90 802 2 24 45 75 66
十位排序: 802 2 24 45 66 170 75 90
百位排序: 2 24 45 66 75 90 170 802

计数排序

     重点:统计元素出现次数
    使用场景:数据范围不大的非负整数
    时间复杂度:O(n+k)(k是数据范围)

    void countingSort(int arr[], int n) {// 找出数组中的最大值int max = arr[0];for (int i = 1; i < n; i++) {if (arr[i] > max) {max = arr[i];}}// 创建计数数组并初始化int count[max+1];for (int i = 0; i <= max; i++) {count[i] = 0;}// 统计每个元素出现次数for (int i = 0; i < n; i++) {count[arr[i]]++;}// 根据计数数组重构原数组int index = 0;for (int i = 0; i <= max; i++) {while (count[i] > 0) {arr[index++] = i;count[i]--;}}
    }
    计数排序调用示例(直观展示)

     

    int arr[] = {4, 2, 2, 8, 3, 3, 1};
    int n = sizeof(arr)/sizeof(arr[0]);printf("原始数组: ");
    printArray(arr, n); // 输出: 4 2 2 8 3 3 1countingSort(arr, n);printf("排序后数组: ");
    printArray(arr, n); // 输出: 1 2 2 3 3 4 8//流程
    count数组: [0,1,2,2,1,0,0,0,1] 
    (表示1出现1次,2出现2次...8出现1次)

    • 桶排序

    重点:将数据分到有限数量的桶中,每个桶单独排序
    使用场景:数据均匀分布在一定范围内
    时间复杂度:O(n+k)(k是桶的数量)

    // 简单桶排序实现(假设输入数据在[0,100)范围内)
    void bucketSort(int arr[], int n) {// 创建10个桶(每个桶负责10个数的范围)const int bucket_num = 10;int buckets[bucket_num][n]; // 每个桶最多可能存储n个元素int bucket_sizes[bucket_num] = {0}; // 记录每个桶当前大小// 将元素分配到桶中for (int i = 0; i < n; i++) {int bucket_idx = arr[i] / 10; // 确定桶索引buckets[bucket_idx][bucket_sizes[bucket_idx]++] = arr[i];}// 对每个桶进行排序(这里使用插入排序)for (int i = 0; i < bucket_num; i++) {insertionSort(buckets[i], bucket_sizes[i]);}// 将排序后的桶合并回原数组int index = 0;for (int i = 0; i < bucket_num; i++) {for (int j = 0; j < bucket_sizes[i]; j++) {arr[index++] = buckets[i][j];}}
    }
    桶排序调用示例(直观展示)
     int arr[] = {29, 25, 3, 49, 9, 37, 21, 43};int n = sizeof(arr)/sizeof(arr[0]);int bucketSize = 5; // 桶数量printf("原始数组: ");for (int i = 0; i < n; i++) printf("%d ", arr[i]);printf("\n\n");bucketSort(arr, n, bucketSize);printf("\n最终结果: ");for (int i = 0; i < n; i++) printf("%d ", arr[i]);//流程
    原始数组: 29 25 3 49 9 37 21 43 分桶结果:
    桶0: 3 9      // 0-9.8
    桶1: 25 21    // 9.8-19.6
    桶2: 29       // 19.6-29.4
    桶3: 37       // 29.4-39.2
    桶4: 49 43    // 39.2-49最终结果: 3 9 21 25 29 37 43 49

    • 堆排序

    重点:利用堆数据结构进行选择排序
    使用场景:需要原地排序且O(nlogn)时间复杂度
    时间复杂度:O(nlogn)

    // 调整堆(大顶堆)
    void heapify(int arr[], int n, int i) {int largest = i;    // 初始化最大元素为根int left = 2*i + 1; // 左子节点int right = 2*i + 2; // 右子节点// 如果左子节点大于根if (left < n && arr[left] > arr[largest]) {largest = left;}// 如果右子节点大于当前最大值if (right < n && arr[right] > arr[largest]) {largest = right;}// 如果最大值不是根if (largest != i) {// 交换int temp = arr[i];arr[i] = arr[largest];arr[largest] = temp;// 递归调整受影响的子堆heapify(arr, n, largest);}
    }void heapSort(int arr[], int n) {// 构建堆(从最后一个非叶子节点开始)for (int i = n/2 - 1; i >= 0; i--) {heapify(arr, n, i);}// 一个个从堆顶取出元素for (int i = n-1; i > 0; i--) {// 将当前根(最大值)移动到数组末尾int temp = arr[0];arr[0] = arr[i];arr[i] = temp;// 调整剩余元素的堆heapify(arr, i, 0);}
    }
     堆排序调用示例(直观展示)
    int arr[] = {12, 11, 13, 5, 6, 7};
    int n = sizeof(arr)/sizeof(arr[0]);printf("原始数组: ");
    printArray(arr, n); // 输出: 12 11 13 5 6 7heapSort(arr, n);printf("排序后数组: ");
    printArray(arr, n); // 输出: 5 6 7 11 12 13//建堆过程
    初始: 12(0)/    \11(1)  13(2)/ \  5(3)6(4)调整后堆顶: 13
    交换13和末尾元素7...

    使用场景选择

    排序算法最佳场景时间复杂度(平均)空间复杂度稳定性
    冒泡排序教学使用O(n²)O(1)稳定
    选择排序小数据量O(n²)O(1)不稳定
    插入排序基本有序小数据O(n²)O(1)稳定
    希尔排序中等规模数据O(n^(3/2))O(1)不稳定
    归并排序需要稳定排序O(nlogn)O(n)稳定
    快速排序通用高效排序O(nlogn)O(logn)不稳定
    堆排序需要原地排序O(nlogn)O(1)不稳定
    计数排序小范围整数O(n+k)O(k)稳定
    桶排序均匀分布数据O(n+k)O(n+k)稳定
    基数排序多位数整数O(nk)O(n+k)稳定

     

    相关文章:

  • MySQL 命令行的核心操作命令详解
  • 始理解NLP:我的第一章学习心得
  • SQL注入漏洞-下篇
  • Hive 3.x集成Apache Ranger:打造精细化数据权限管理体系
  • 【Unity笔记】Unity URP 渲染中的灯光数量设置— 场景、使用方法与渲染原理详解
  • Mac Mini M4 安装 jdk8 以及 隐藏 设置内的Java菜单
  • Mac 安装JD-GUI
  • Windows PPT/word怎么pdf不降低分辨率,插入可编辑
  • CSS 选择器、PxCook软件、盒子模型
  • UE5 自动寻路AI Move To节点学习笔记
  • 智能开发工具RubyMine v2025.1正式发布——增强AI功能部署
  • 随着数字资产的增长,香港推进稳定币监管
  • 【Mac 上离线安装 ADB 工具】
  • 2.5G PHY master和slave的tx/rx控制
  • React源码系列之Hooks(useCallback、useMemo)
  • 内存泄漏到底是个什么东西?如何避免内存泄漏
  • 1.2 人工智能的分类
  • 基于传统机器学习SVM支持向量机进行分类和目标检测-视频介绍下自取
  • Vue 3 自定义指令进阶:打造复用性极高的 DOM 交互逻辑
  • 【Java学习笔记】Collections工具类
  • 做个网站要钱吗/论坛seo教程
  • 网站目的及功能定位/设计网站排行榜前十名
  • 自创字 网站/seo外链论坛
  • 微信网站与响应式网站/seo教程网站优化
  • 网站建设中的咨询服务/无锡营销型网站建设
  • 电子网站模板/天津百度关键词推广公司