首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >深入理解 C 语言冒泡排序:从代码实现到原理剖析

深入理解 C 语言冒泡排序:从代码实现到原理剖析

作者头像
fashion
发布2025-12-31 17:15:33
发布2025-12-31 17:15:33
250
举报

在 C 语言的学习旅程中,排序算法是绕不开的重要知识点,而冒泡排序作为最基础、最易理解的排序算法之一,更是初学者入门的绝佳选择。今天,我们就从一段具体的 C 语言冒泡排序代码出发,一步步拆解其实现逻辑、核心原理以及可优化的方向,带大家真正掌握冒泡排序的精髓。

一、代码功能初览:让无序数组 “归位”

首先,我们先来看这段完整的 C 语言代码。它的核心功能非常明确:定义一个包含 10 个元素的无序数组arr[10] = {1,2,3,4,5,6,7,8,9,0},通过冒泡排序算法将其按升序排列,最后再将排序后的数组打印输出。

在main函数中,有一个关键操作 —— 计算数组的元素个数sz = sizeof(arr)/sizeof(arr[0])。这里需要特别注意,sizeof(arr)计算的是整个数组所占的字节数,而sizeof(arr[0])计算的是数组首元素所占的字节数,两者相除才能得到数组的元素个数。很多初学者会直接在bubble_sort函数中使用sizeof(arr)来计算元素个数,但这是错误的,因为数组作为函数参数传递时,会退化为指针,此时sizeof(arr)计算的是指针的大小(在 32 位系统中为 4 字节,64 位系统中为 8 字节),而非数组的大小,这也是代码注释中强调 “arr 是首元素地址,在函数中不能用其求 sz” 的原因。

当我们运行这段代码时,最终会在控制台输出0 1 2 3 4 5 6 7 8 9,这表明冒泡排序算法成功将原本末尾的0“冒泡” 到了数组开头,实现了数组的升序排列。

二、冒泡排序原理:“相邻比较,逐步归位”

要理解这段代码,关键在于搞懂冒泡排序的核心原理。冒泡排序的本质是通过相邻元素之间的比较和交换,将最大(或最小)的元素逐步 “冒泡” 到数组的末尾(或开头),就像水中的气泡逐渐上浮一样。

1. 外层循环:控制 “冒泡趟数”

代码中的外层循环for(i=0;i<sz-1;i++)控制的是冒泡排序的 “趟数”。为什么是sz-1趟而不是sz趟呢?我们可以这样理解:对于一个包含sz个元素的数组,当我们完成sz-1趟排序后,前sz-1个元素已经按照升序排列好了,那么最后一个元素自然就是最大的元素,不需要再进行第sz趟排序了。

比如在我们的例子中,数组有 10 个元素,外层循环会执行 9 趟。第一趟排序会将最大的元素9“冒泡” 到数组的末尾(索引 9 的位置);第二趟排序会将第二大的元素8“冒泡” 到索引 8 的位置;以此类推,直到第九趟排序,将第二小的元素1“冒泡” 到索引 1 的位置,此时数组就完全有序了。

2. 内层循环:实现 “相邻比较与交换”

内层循环for(j=0;j<=sz-1-i;j++)则负责每一趟排序中的相邻元素比较与交换。这里的sz-1-i是一个非常关键的优化点,代码注释中也提到 “-i 可以提高效率,每次循环都把排好的数字省略了;减小了运算”。

为什么要减去i呢?因为在外层循环执行i趟之后,数组末尾的i个元素已经是有序的了(它们是前i趟排序中 “冒泡” 到末尾的最大i个元素),不需要再对这i个元素进行比较。比如在第一趟排序(i=0)时,内层循环需要比较到sz-1的位置(即索引 9),因为此时还没有任何元素有序;在第二趟排序(i=1)时,数组末尾的 1 个元素(索引 9)已经有序,所以内层循环只需要比较到sz-1-1 = sz-2的位置(即索引 8);以此类推,每多执行一趟外层循环,内层循环的比较次数就会减少 1 次,大大提高了排序的效率。

在每次内层循环中,代码会判断当前元素arr[j]是否大于下一个元素arr[j+1]。如果满足条件,就通过一个临时变量t实现两个元素的交换,将较大的元素往后移动一位。通过这样一次次的相邻比较和交换,每一趟排序都会将当前未排序部分的最大元素 “冒泡” 到未排序部分的末尾。

三、代码优化:让排序更高效

虽然这段代码已经实现了冒泡排序的基本功能,但在实际应用中,我们还可以对其进行进一步优化,让排序效率更高。

1. 增加 “有序标记”,避免无效循环

在某些情况下,数组可能在完成sz-1趟排序之前就已经变得有序了。比如数组{1,2,3,4,5,6,7,8,0,9},在第一趟排序将9“冒泡” 到末尾后,第二趟排序将8“冒泡” 到索引 8 的位置,此时数组已经变成{0,1,2,3,4,5,6,7,8,9},后续的 7 趟排序都是无效的。

为了避免这种无效循环,我们可以增加一个 “有序标记”。具体做法是:在每一趟外层循环开始前,定义一个布尔变量is_sorted = 1(假设 1 表示有序,0 表示无序);在每一次内层循环的元素交换后,将is_sorted设为 0;每一趟外层循环结束后,判断is_sorted的值,如果is_sorted仍然为 1,说明这一趟排序中没有发生任何元素交换,数组已经有序,此时可以直接跳出外层循环,结束排序。

优化后的bubble_sort函数代码如下:

void bubble_sort(int arr[], int sz) {

int i = 0;

for (i = 0; i < sz - 1; i++) {

int is_sorted = 1; // 有序标记,初始化为有序

int j = 0;

for (j = 0; j <= sz - 1 - i; j++) {

if (arr[j] > arr[j + 1]) {

int t = arr[j];

arr[j] = arr[j + 1];

arr[j + 1] = t;

is_sorted = 0; // 发生交换,说明数组无序

}

}

if (is_sorted == 1) {

break; // 数组已经有序,跳出循环

}

}

}

2. 记录 “最后一次交换位置”,进一步减少比较次数

除了增加 “有序标记”,我们还可以记录每一趟排序中 “最后一次交换的位置”。因为在最后一次交换位置之后的元素,在这一趟排序中没有发生任何交换,说明它们已经是有序的了,下一趟排序时,内层循环只需要比较到这个 “最后一次交换位置” 即可,而不需要再比较到sz-1-i的位置。

这种优化方式特别适用于数组中大部分元素已经有序的情况,可以进一步减少内层循环的比较次数,提高排序效率。

四、总结:冒泡排序的价值与意义

虽然冒泡排序的时间复杂度为O(n²),在处理大规模数据时效率较低,但其依然具有重要的学习价值。

首先,冒泡排序的原理非常简单直观,通过相邻元素的比较和交换实现排序,非常适合初学者理解排序算法的基本思想 —— 通过重复操作逐步将数据变得有序。其次,这段 C 语言代码中涉及了数组作为函数参数传递、sizeof运算符的使用、循环结构的嵌套等多个 C 语言的核心知识点,掌握这段代码的实现与优化,能够帮助初学者加深对 C 语言基础语法的理解和运用。

最后,冒泡排序的优化思路(如增加有序标记、记录最后一次交换位置)也为我们学习更复杂的排序算法(如插入排序、希尔排序)奠定了基础,让我们明白 “优化” 在算法设计中的重要性。

希望通过今天对这段冒泡排序代码的深入剖析,大家能够真正掌握冒泡排序的原理和实现,同时也能对 C 语言的相关知识点有更清晰的认识。在后续的学习中,也可以尝试将冒泡排序修改为降序排列,或者用其处理不同类型的数组(如字符数组),进一步巩固所学知识。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、代码功能初览:让无序数组 “归位”
  • 二、冒泡排序原理:“相邻比较,逐步归位”
    • 1. 外层循环:控制 “冒泡趟数”
    • 2. 内层循环:实现 “相邻比较与交换”
  • 三、代码优化:让排序更高效
    • 1. 增加 “有序标记”,避免无效循环
    • 2. 记录 “最后一次交换位置”,进一步减少比较次数
  • 四、总结:冒泡排序的价值与意义
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档