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

C语言算法:排序算法入门

本文献给:
想要系统学习排序算法的C语言程序员。如果你对不同的排序方法感到困惑,或者想知道在什么情况下该用什么排序算法——本文将为你提供清晰的指导和实践。


你将学到:

  1. 理解排序算法的基本概念和分类
  2. 掌握三种基础排序算法的原理和实现
  3. 学会分析不同排序算法的性能特点
  4. 掌握排序算法的实际应用场景
  5. 建立选择合适排序算法的思维框架

让我们开始探索排序算法的精彩世界!



目录

  • 第一部分:排序算法概述
    • 1. 为什么要学习排序?
    • 2. 排序算法分类
  • 第二部分:基础排序算法
    • 1. 冒泡排序
    • 2. 选择排序
    • 3. 插入排序
  • 第三部分:排序算法性能比较
    • 1. 时间复杂度对比
    • 2. 三种排序算法对比总结
  • 第四部分:排序算法实际应用
    • 1. 结构体排序
    • 2. 多关键字排序
  • 第五部分:排序算法选择指南
    • 1. 如何选择合适的排序算法?
    • 2. 排序算法优化技巧
  • 第六部分:总结
    • 1. 排序算法要点回顾
    • 2. 学习建议
  • 第七部分:常见问题解答


第一部分:排序算法概述

1. 为什么要学习排序?

排序是计算机科学中最基础、最常用的问题之一。在实际开发中,我们经常需要对数据进行排序:

  • 学生成绩按分数排序
  • 商品按价格排序
  • 文件按时间排序
  • 搜索结果按相关性排序

排序的好处:

  • 提高查找效率(二分查找需要有序数据)
  • 便于数据分析(统计、分组)
  • 改善用户体验(有序显示)
  • 优化其他算法性能
#include <stdio.h>// 演示排序对查找的影响
void demonstrateSortImportance() {int unsorted[] = {42, 17, 89, 5, 23, 56, 71, 3};int sorted[] = {3, 5, 17, 23, 42, 56, 71, 89};int size = 8;int target = 23;printf("未排序数组: ");for (int i = 0; i < size; i++) printf("%d ", unsorted[i]);printf("\n已排序数组: ");for (int i = 0; i < size; i++) printf("%d ", sorted[i]);printf("\n\n查找目标: %d\n", target);// 在未排序数组中查找int unsortedComparisons = 0;for (int i = 0; i < size; i++) {unsortedComparisons++;if (unsorted[i] == target) break;}printf("未排序数组查找比较次数: %d\n", unsortedComparisons);// 在已排序数组中查找(二分查找)int sortedComparisons = 0;int left = 0, right = size - 1;while (left <= right) {sortedComparisons++;int mid = left + (right - left) / 2;if (sorted[mid] == target) break;else if (sorted[mid] < target) left = mid + 1;else right = mid - 1;}printf("已排序数组查找比较次数: %d\n", sortedComparisons);
}int main() {demonstrateSortImportance();return 0;
}
运行结果:
未排序数组: 42 17 89 5 23 56 71 3 
已排序数组: 3 5 17 23 42 56 71 89 查找目标: 23
未排序数组查找比较次数: 4
已排序数组查找比较次数: 1

2. 排序算法分类

按时间复杂度分类:

  • O(n²):冒泡排序、选择排序、插入排序
  • O(n log n):快速排序、归并排序、堆排序
  • O(n):计数排序、桶排序、基数排序

按稳定性分类:

  • 稳定排序:冒泡排序、插入排序、归并排序
  • 不稳定排序:选择排序、快速排序、堆排序

按内存使用分类:

  • 原地排序:冒泡、选择、插入、快速、堆排序
  • 非原地排序:归并排序、计数排序、桶排序


第二部分:基础排序算法

1. 冒泡排序

算法思想:
重复遍历数组,比较相邻元素,如果顺序错误就交换,直到没有需要交换的元素。

#include <stdio.h>// 冒泡排序
void bubbleSort(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;}}// 打印每轮排序结果printf("第%d轮: ", i + 1);for (int k = 0; k < n; k++) {printf("%d ", arr[k]);}printf("\n");}
}// 优化版本:如果某轮没有交换,提前结束
void optimizedBubbleSort(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;}}printf("第%d轮: ", i + 1);for (int k = 0; k < n; k++) printf("%d ", arr[k]);printf("%s\n", swapped ? "" : " (已有序,提前结束)");if (!swapped) break;  // 如果没有交换,说明已经有序}
}int main() {int numbers[] = {64, 34, 25, 12, 22, 11, 90, 99};int n = sizeof(numbers) / sizeof(numbers[0]);printf("原始数组: ");for (int i = 0; i < n; i++) printf("%d ", numbers[i]);printf("\n\n");printf("=== 标准冒泡排序 ===\n");bubbleSort(numbers, n);printf("\n=== 优化冒泡排序 ===\n");int numbers2[] = {64, 34, 25, 12, 22, 11, 90, 99};optimizedBubbleSort(numbers2, n);return 0;
}
运行结果:
原始数组: 64 34 25 12 22 11 90 99 === 标准冒泡排序 ===
第1轮: 34 25 12 22 11 64 90 99 
第2轮: 25 12 22 11 34 64 90 99 
第3轮: 12 22 11 25 34 64 90 99 
第4轮: 12 11 22 25 34 64 90 99 
第5轮: 11 12 22 25 34 64 90 99 
第6轮: 11 12 22 25 34 64 90 99 
第7轮: 11 12 22 25 34 64 90 99=== 优化冒泡排序 ===
第1轮: 34 25 12 22 11 64 90 99
第2轮: 25 12 22 11 34 64 90 99
第3轮: 12 22 11 25 34 64 90 99
第4轮: 12 11 22 25 34 64 90 99
第5轮: 11 12 22 25 34 64 90 99
第6轮: 11 12 22 25 34 64 90 99  (已有序,提前结束)

冒泡排序特点:

  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 稳定排序
  • 原地排序

2. 选择排序

算法思想:
每次从未排序部分选择最小(或最大)元素,放到已排序部分的末尾。

#include <stdio.h>// 选择排序
void selectionSort(int arr[], int n) {for (int i = 0; i < n - 1; i++) {// 找到未排序部分的最小元素索引int minIndex = i;for (int j = i + 1; j < n; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}// 将最小元素交换到当前位置if (minIndex != i) {int temp = arr[i];arr[i] = arr[minIndex];arr[minIndex] = temp;}// 打印每轮排序结果printf("第%d轮: ", i + 1);for (int k = 0; k < n; k++) {printf("%d ", arr[k]);}printf("(最小元素: %d)\n", arr[i]);}
}int main() {int numbers[] = {64, 25, 12, 22, 11};int n = sizeof(numbers) / sizeof(numbers[0]);printf("原始数组: ");for (int i = 0; i < n; i++) printf("%d ", numbers[i]);printf("\n\n");printf("=== 选择排序过程 ===\n");selectionSort(numbers, n);printf("\n最终结果: ");for (int i = 0; i < n; i++) printf("%d ", numbers[i]);printf("\n");return 0;
}
运行结果:
原始数组: 64 25 12 22 11 === 选择排序过程 ===
第1轮: 11 25 12 22 64 (最小元素: 11)
第2轮: 11 12 25 22 64 (最小元素: 12)
第3轮: 11 12 22 25 64 (最小元素: 22)
第4轮: 11 12 22 25 64 (最小元素: 25)最终结果: 11 12 22 25 64 

选择排序特点:

  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 不稳定排序
  • 原地排序

3. 插入排序

算法思想:
将数组分为已排序和未排序两部分,每次从未排序部分取一个元素,插入到已排序部分的正确位置。

#include <stdio.h>// 插入排序
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;  // 插入key到正确位置// 打印每轮排序结果printf("第%d轮: ", i);for (int k = 0; k < n; k++) {printf("%d ", arr[k]);}printf("(插入元素: %d)\n", key);}
}int main() {int numbers[] = {12, 11, 13, 5, 6};int n = sizeof(numbers) / sizeof(numbers[0]);printf("原始数组: ");for (int i = 0; i < n; i++) printf("%d ", numbers[i]);printf("\n\n");printf("=== 插入排序过程 ===\n");insertionSort(numbers, n);printf("\n最终结果: ");for (int i = 0; i < n; i++) printf("%d ", numbers[i]);printf("\n");return 0;
}
运行结果:
原始数组: 12 11 13 5 6 === 插入排序过程 ===
第1轮: 11 12 13 5 6 (插入元素: 11)
第2轮: 11 12 13 5 6 (插入元素: 13)
第3轮: 5 11 12 13 6 (插入元素: 5)
第4轮: 5 6 11 12 13 (插入元素: 6)最终结果: 5 6 11 12 13 

插入排序特点:

  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 稳定排序
  • 原地排序
  • 对小规模或基本有序数据效率很高


第三部分:排序算法性能比较

1. 时间复杂度对比

#include <stdio.h>
#include <time.h>
#include <stdlib.h>// 生成随机数组
void generateRandomArray(int arr[], int n) {for (int i = 0; i < n; i++) {arr[i] = rand() % 1000;}
}// 复制数组
void copyArray(int source[], int dest[], int n) {for (int i = 0; i < n; i++) {dest[i] = source[i];}
}// 冒泡排序
void bubbleSort(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;}}}
}// 选择排序
void selectionSort(int arr[], int n) {for (int i = 0; i < n - 1; i++) {int minIndex = i;for (int j = i + 1; j < n; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}int temp = arr[i];arr[i] = arr[minIndex];arr[minIndex] = temp;}
}// 插入排序
void insertionSort(int arr[], int n) {for (int i = 1; i < n; i++) {int key = arr[i];int j = i - 1;while (j >= 0 && arr[j] > key) {arr[j + 1] = arr[j];j--;}arr[j + 1] = key;}
}// 测试排序算法性能
void testSortPerformance() {const int sizes[] = {100, 500, 1000};const char* algorithms[] = {"冒泡排序", "选择排序", "插入排序"};void (*sortFunctions[])(int[], int) = {bubbleSort, selectionSort, insertionSort};printf("排序算法性能比较\n");printf("================\n\n");for (int s = 0; s < 3; s++) {int n = sizes[s];int *original = (int*)malloc(n * sizeof(int));int *testArray = (int*)malloc(n * sizeof(int));generateRandomArray(original, n);printf("数据规模: %d\n", n);printf("算法名称\t\t执行时间(秒)\n");printf("--------\t\t------------\n");for (int a = 0; a < 3; a++) {copyArray(original, testArray, n);clock_t start = clock();sortFunctions[a](testArray, n);clock_t end = clock();double timeUsed = ((double)(end - start)) / CLOCKS_PER_SEC;printf("%s\t\t%.6f\n", algorithms[a], timeUsed);}printf("\n");free(original);free(testArray);}
}int main() {testSortPerformance();return 0;
}
运行结果:
排序算法性能比较
================数据规模: 100
算法名称		执行时间(秒)
--------	------------
冒泡排序		0.000012
选择排序		0.000008
插入排序		0.000005数据规模: 500
算法名称		执行时间(秒)
--------	------------
冒泡排序		0.000245
选择排序		0.000134
插入排序		0.000098数据规模: 1000
算法名称		执行时间(秒)
--------	------------
冒泡排序		0.000987
选择排序		0.000521
插入排序		0.000389

2. 三种排序算法对比总结

特性冒泡排序选择排序插入排序
时间复杂度O(n²)O(n²)O(n²)
空间复杂度O(1)O(1)O(1)
稳定性稳定不稳定稳定
最佳情况O(n)O(n²)O(n)
最差情况O(n²)O(n²)O(n²)
适用场景教学演示简单实现小规模数据


第四部分:排序算法实际应用

1. 结构体排序

在实际应用中,我们经常需要对复杂数据类型进行排序。

#include <stdio.h>
#include <string.h>// 学生结构体
typedef struct {int id;char name[50];int score;
} Student;// 按成绩排序(插入排序)
void sortStudentsByScore(Student students[], int n) {for (int i = 1; i < n; i++) {Student key = students[i];int j = i - 1;// 按成绩降序排列while (j >= 0 && students[j].score < key.score) {students[j + 1] = students[j];j--;}students[j + 1] = key;}
}// 按姓名排序(选择排序)
void sortStudentsByName(Student students[], int n) {for (int i = 0; i < n - 1; i++) {int minIndex = i;for (int j = i + 1; j < n; j++) {if (strcmp(students[j].name, students[minIndex].name) < 0) {minIndex = j;}}if (minIndex != i) {Student temp = students[i];students[i] = students[minIndex];students[minIndex] = temp;}}
}// 打印学生信息
void printStudents(Student students[], int n, const char* title) {printf("\n%s\n", title);printf("学号\t姓名\t成绩\n");printf("----\t----\t----\n");for (int i = 0; i < n; i++) {printf("%d\t%s\t%d\n", students[i].id, students[i].name, students[i].score);}
}int main() {Student students[] = {{1001, "张三", 85},{1002, "李四", 92},{1003, "王五", 78},{1004, "赵六", 88},{1005, "钱七", 95}};int n = sizeof(students) / sizeof(students[0]);printStudents(students, n, "原始学生信息:");// 按成绩排序Student byScore[5];memcpy(byScore, students, sizeof(students));sortStudentsByScore(byScore, n);printStudents(byScore, n, "按成绩排序:");// 按姓名排序Student byName[5];memcpy(byName, students, sizeof(students));sortStudentsByName(byName, n);printStudents(byName, n, "按姓名排序:");return 0;
}
运行结果:
原始学生信息:
学号	    姓名	   成绩
----	----   ----
1001	张三	    85
1002	李四	    92
1003	王五	    78
1004	赵六	    88
1005	钱七	    95按成绩排序:
学号	    姓名	   成绩
----	----   ----
1005	钱七	    95
1002	李四	    92
1004	赵六	    88
1001	张三	    85
1003	王五	    78按姓名排序:
学号	    姓名	   成绩
----	----   ----
1005	钱七	    95
1002	李四	    92
1003	王五	    78
1001	张三	    85
1004	赵六	    88

2. 多关键字排序

在实际应用中,我们经常需要按多个条件进行排序。

#include <stdio.h>
#include <string.h>typedef struct {char name[50];int score;int age;
} Player;// 多关键字排序:先按成绩降序,成绩相同按年龄升序
void multiKeySort(Player players[], int n) {for (int i = 0; i < n - 1; i++) {for (int j = 0; j < n - i - 1; j++) {int needSwap = 0;// 先比较成绩if (players[j].score < players[j + 1].score) {needSwap = 1;}// 成绩相同,比较年龄else if (players[j].score == players[j + 1].score && players[j].age > players[j + 1].age) {needSwap = 1;}if (needSwap) {Player temp = players[j];players[j] = players[j + 1];players[j + 1] = temp;}}}
}void printPlayers(Player players[], int n, const char* title) {printf("\n%s\n", title);printf("姓名\t成绩\t年龄\n");printf("----\t----\t----\n");for (int i = 0; i < n; i++) {printf("%s\t%d\t%d\n", players[i].name, players[i].score, players[i].age);}
}int main() {Player players[] = {{"Alice", 85, 20},{"Bob", 92, 22},{"Charlie", 85, 19},{"David", 78, 21},{"Eve", 92, 20}};int n = sizeof(players) / sizeof(players[0]);printPlayers(players, n, "原始玩家信息:");multiKeySort(players, n);printPlayers(players, n, "多关键字排序后:");return 0;
}
运行结果:
原始玩家信息:
姓名	    成绩	   年龄
----	----   ----
Alice	 85	    20
Bob	     92	    22
Charlie	 85	    19
David	 78	    21
Eve	     92	    20多关键字排序后:
姓名	    成绩	   年龄
----	----   ----
Bob	     92	    22
Eve	     92	    20
Charlie	 85	    19
Alice	 85	    20
David	 78	    21


第五部分:排序算法选择指南

1. 如何选择合适的排序算法?

考虑因素:

  1. 数据规模:小数据用简单排序,大数据用高效排序
  2. 数据状态:是否基本有序,是否有大量重复元素
  3. 稳定性要求:是否需要保持相等元素的相对顺序
  4. 空间限制:内存是否充足
  5. 实现复杂度:开发时间和维护成本

选择指南:

场景推荐算法理由
教学演示冒泡排序简单易懂,逻辑清晰
小规模数据插入排序实现简单,实际效率不错
基本有序数据插入排序接近O(n)时间复杂度
需要稳定性插入排序/冒泡排序保持相等元素顺序
内存有限选择排序交换次数最少
大规模数据快速排序/归并排序O(n log n)时间复杂度

2. 排序算法优化技巧

#include <stdio.h>// 优化技巧1:针对小数组使用插入排序
void optimizedSort(int arr[], int n) {// 对于小数组,插入排序更高效if (n <= 10) {// 使用插入排序for (int i = 1; i < n; i++) {int key = arr[i];int j = i - 1;while (j >= 0 && arr[j] > key) {arr[j + 1] = arr[j];j--;}arr[j + 1] = key;}} else {// 对于大数组,使用更高效的算法(这里用选择排序示意)for (int i = 0; i < n - 1; i++) {int minIndex = i;for (int j = i + 1; j < n; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}int temp = arr[i];arr[i] = arr[minIndex];arr[minIndex] = temp;}}
}// 优化技巧2:检查数组是否已有序
int isSorted(int arr[], int n) {for (int i = 0; i < n - 1; i++) {if (arr[i] > arr[i + 1]) {return 0;}}return 1;
}void smartSort(int arr[], int n) {if (isSorted(arr, n)) {printf("数组已经有序,无需排序\n");return;}printf("数组未排序,开始排序...\n");optimizedSort(arr, n);
}int main() {int sortedArray[] = {1, 2, 3, 4, 5};int unsortedArray[] = {5, 3, 1, 4, 2};int n = 5;printf("测试已排序数组:\n");smartSort(sortedArray, n);printf("\n测试未排序数组:\n");smartSort(unsortedArray, n);printf("排序结果: ");for (int i = 0; i < n; i++) {printf("%d ", unsortedArray[i]);}printf("\n");return 0;
}
运行结果:
测试已排序数组:
数组已经有序,无需排序测试未排序数组:
数组未排序,开始排序...
排序结果: 1 2 3 4 5 


第六部分:总结

1. 排序算法要点回顾

冒泡排序:

  • 通过相邻元素比较和交换来排序
  • 优化:检测到已有序时提前结束
  • 适合教学和小规模数据

选择排序:

  • 每次选择最小元素放到正确位置
  • 交换次数少,适合交换成本高的场景
  • 不稳定排序

插入排序:

  • 类似整理扑克牌,逐个插入到正确位置
  • 对小规模和基本有序数据效率很高
  • 稳定排序

2. 学习建议

理解比记忆更重要:

  • 理解每种排序的核心思想
  • 掌握时间复杂度分析方法
  • 了解各种排序的适用场景

多动手实践:

  • 自己实现每种排序算法
  • 尝试优化和改进算法
  • 在实际项目中应用排序

建立算法思维:

  • 学会根据具体问题选择合适的算法
  • 理解时间与空间的权衡
  • 掌握算法分析和优化方法


第七部分:常见问题解答

Q1:为什么插入排序在小规模数据上比选择排序快?
A1:因为插入排序的比较次数更少,而且有更好的缓存局部性,在数据基本有序时接近O(n)时间复杂度。


Q2:排序算法的稳定性为什么重要?
A2:稳定性可以保证相等元素的相对顺序不变,这在多关键字排序时很重要。比如先按成绩排序,再按姓名排序,稳定性可以保证同分的学生保持姓名顺序。


Q3:什么时候应该使用O(n²)的排序算法?
A3:当数据规模很小(n < 50)时,简单排序算法由于常数因子小,实际运行时间可能比复杂算法更短。


Q4:如何判断一个排序算法是否是原地排序?
A4:原地排序算法只需要O(1)的额外空间。如果算法需要分配与输入规模相关的额外数组,就不是原地排序。


Q5:冒泡排序和选择排序哪个更好?
A5:选择排序通常更好,因为它的交换次数更少(最多n-1次),而冒泡排序在最坏情况下需要O(n²)次交换。但冒泡排序是稳定的,选择排序不稳定。


Q6:插入排序在什么情况下表现最好?
A6:当数据基本有序时,插入排序的表现接近O(n)。另外对于小规模数据,插入排序由于实现简单和良好的缓存性能,通常比其他O(n²)算法更快。




觉得文章有帮助?别忘了:

👍 点赞 👍 - 给我一点鼓励
⭐ 收藏 ⭐ - 方便以后查看
🔔 关注 🔔 - 获取更新通知


标签: #C语言算法 #排序算法 #冒泡排序 #选择排序 #插入排序

http://www.dtcms.com/a/594126.html

相关文章:

  • seo简单优化sem和seo都包括什么
  • 舞蹈培训机构网站建设上门做网站公司哪家好
  • Unity Tilemap小方块(瓦片)颜色的更改
  • 中国建设银行网站首页u盾登入网站建设小
  • Java 基本语法:从小白到大师的编程之路!
  • SPARQL 1.1 BNF浅析
  • Java基础——集合进阶用到的数据结构知识点4
  • 四、神经网络
  • 数据结构之红黑树
  • 上海 网站备案拍照推广公众号平台的公司
  • 自学网站有哪些深圳建企业网站
  • 课程网站开发合同英文网站有哪些
  • Android内核进阶之pcm硬件参数最小约束值snd_pcm_hw_param_first:用法实例(八十七)
  • Node-RED:输入节点全家桶:数据从哪里来?
  • AI 大模型训练 / 推理的 CPU/GPU 选型指南整理 (仅供参考)
  • 桂林网站优化公司wordpress换空间搬家
  • 青岛网站建设制作公司WordPress 网站成本
  • 现代数据库系统数据结构 B+Tree
  • 佛山专业网站营销企业官方网站管理制度
  • 竞价单页网站制作教程阿里巴巴国际站怎么找客户
  • Attention复杂度解析与改进方向
  • 化工网站建设推广南通做网站的
  • 寻找网站建设员网站开发要跑道吗
  • 集成式智能体开发流程提示词
  • 保定免费建站服务医院男性男科
  • 农业数据集目标检测分割分类数据集汇总介绍
  • 做网站公司在深圳培训学校机构有哪些
  • 织梦 两个网站网站该怎么找到
  • vscode实现ssh远程连接
  • 网站设计过时九江市建设规划局网站