冒泡排序与数组传递全解析 一维二维指针数组及二级指针传参指南
一、冒泡排序核心知识点
冒泡排序是一种简单的交换排序算法,核心思想是通过相邻元素的比较与交换,让较大(或较小)的元素像气泡一样逐步"浮"到数组末端。以升序排序为例,步骤如下:
-
比较相邻元素:从数组第一个元素开始,依次比较相邻的两个元素(如arr[i]和arr[i+1])。
-
交换逆序元素:如果前一个元素大于后一个元素(arr[i] > arr[i+1]),则交换它们的位置。
-
重复多轮:每一轮排序后,最大的元素会"沉"到当前未排序部分的末尾。因此,若有n个元素,需进行n-1轮排序(最后一个元素无需再比较)。
-
优化:若某一轮未发生交换,说明数组已有序,可提前结束排序。
二、传递一维数组给冒泡排序函数
知识点
C语言中,数组作为函数参数时会退化为指针 ,因此传递一维数组时需同时传递数组长度(编译器无法通过数组名获取长度)。函数参数可写为int arr[](等价于int *arr),接收数组首地址。
示例代码(含注释)
cpp
#include <stdio.h>
// 冒泡排序函数:对一维数组升序排序
// 参数:arr 数组首地址(退化为指针),len 数组长度
void bubble_sort_1d(int arr[], int len) {
int i, j, temp;
int swapped; // 标记本轮是否发生交换,用于优化
for (i = 0; i < len - 1; i++) { // 共需 len-1 轮排序
swapped = 0; // 初始化为未交换
// 每轮比较次数:未排序部分长度为 len - i,需比较 len - i - 1 次
for (j = 0; j < len - i - 1; j++) {
if (arr[j] > arr[j + 1]) { // 前大后小,交换
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = 1; // 标记发生交换
}
}
if (!swapped) break; // 本轮无交换,数组已有序,提前退出
}
}
// 打印数组
void print_array(int arr[], int len) {
int i;
for (i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int nums[] = {64, 34, 25, 12, 22, 11, 90};
int len = sizeof(nums) / sizeof(nums[0]); // 计算数组长度
printf("排序前数组:");
print_array(nums, len);
bubble_sort_1d(nums, len); // 传递一维数组和长度
printf("排序后数组:");
print_array(nums, len);
return 0;
}
三、传递二维数组给冒泡排序函数
知识点
二维数组传递需注意函数参数必须指定第二维的大小(除非用指针数组或数组指针)。可将二维数组视为"一维数组"(内存连续),对所有元素统一排序。
示例代码(含注释)
cpp
#include <stdio.h>
// 冒泡排序二维数组(整体排序,视为一维数组)
// 参数:arr 二维数组首地址(第二维固定为4),rows 行数,cols 列数
void bubble_sort_2d(int arr[][4], int rows, int cols) {
int total = rows * cols; // 总元素个数
int i, j, temp;
int swapped;
for (i = 0; i < total - 1; i++) {
swapped = 0;
for (j = 0; j < total - i - 1; j++) {
// 计算当前元素的行列坐标(模拟一维索引转二维)
int row1 = j / cols, col1 = j % cols;
int row2 = (j + 1) / cols, col2 = (j + 1) % cols;
if (arr[row1][col1] > arr[row2][col2]) {
temp = arr[row1][col1];
arr[row1][col1] = arr[row2][col2];
arr[row2][col2] = temp;
swapped = 1;
}
}
if (!swapped) break;
}
}
// 打印二维数组
void print_2d_array(int arr[][4], int rows, int cols) {
int i, j;
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main() {
int matrix[3][4] = {{9, 5, 7, 3}, {2, 8, 1, 6}, {4, 0, 10, 12}};
int rows = 3, cols = 4;
printf("排序前二维数组:\n");
print_2d_array(matrix, rows, cols);
bubble_sort_2d(matrix, rows, cols);
printf("排序后二维数组(整体升序):\n");
print_2d_array(matrix, rows, cols);
return 0;
}
四、指针数组与冒泡排序
知识点
指针数组是数组元素为指针变量 的数组(如char *str_arr[])。对指针数组排序时,交换指针 (而非内容)更高效,常用于字符串排序(按字典序)。指针数组名退化为二级指针 (char **)。
示例代码(含注释)
cpp
#include <stdio.h>
#include <string.h>
// 冒泡排序指针数组(交换指针,按字符串字典序升序)
// 参数:str_arr 指针数组首地址(等价于二级指针 char **),len 数组长度
void bubble_sort_str_ptr(char *str_arr[], int len) {
int i, j;
char *temp; // 临时指针(交换指针用)
int swapped;
for (i = 0; i < len - 1; i++) {
swapped = 0;
for (j = 0; j < len - i - 1; j++) {
// strcmp比较字符串:返回正数表示前者大于后者
if (strcmp(str_arr[j], str_arr[j + 1]) > 0) {
temp = str_arr[j]; // 交换指针(核心)
str_arr[j] = str_arr[j + 1];
str_arr[j + 1] = temp;
swapped = 1;
}
}
if (!swapped) break;
}
}
// 打印字符串数组
void print_str_array(char *str_arr[], int len) {
int i;
for (i = 0; i < len; i++) {
printf("%s ", str_arr[i]);
}
printf("\n");
}
int main() {
char *fruits[] = {"banana", "apple", "cherry", "date"}; // 指针数组
int len = sizeof(fruits) / sizeof(fruits[0]);
printf("排序前字符串数组:");
print_str_array(fruits, len);
bubble_sort_str_ptr(fruits, len); // 传递指针数组(退化为二级指针)
printf("排序后字符串数组(字典序升序):");
print_str_array(fruits, len);
return 0;
}
五、二级指针传参与冒泡排序
知识点
二级指针 (如int **pp)是指向指针的指针,用于存储"指针变量的地址"。在数组传递中,二级指针主要用于两种场景:
-
传递指针数组 :指针数组名本身就是二级指针(如
char *str_arr[]等价于char **str_arr)。 -
传递动态二维数组 :通过
malloc动态分配的二维数组(如int **arr),其数组名也是二级指针。
下面通过两个示例讲解二级指针传参的用法。
示例1 二级指针传递指针数组
之前的指针数组排序中,bubble_sort_str_ptr(char *str_arr[], int len) 的参数**char *str_arr[]** 等价于**char **str_arr**(二级指针)。这里显式用二级指针参数重写,更直观展示二级指针传参逻辑。
cpp
#include <stdio.h>
#include <string.h>
// 显式用二级指针参数:pp 指向指针数组的指针(即二级指针)
void bubble_sort_2ptr(char **pp, int len) {
int i, j;
char *temp;
int swapped;
for (i = 0; i < len - 1; i++) {
swapped = 0;
for (j = 0; j < len - i - 1; j++) {
if (strcmp(pp[j], pp[j + 1]) > 0) { // 比较字符串
temp = pp[j]; // 交换指针(通过二级指针访问元素)
pp[j] = pp[j + 1];
pp[j + 1] = temp;
swapped = 1;
}
}
if (!swapped) break;
}
}
int main() {
char *fruits[] = {"banana", "apple", "cherry", "date"};
int len = sizeof(fruits) / sizeof(fruits[0]);
// fruits 是指针数组名,退化为二级指针 char **,直接传递给函数
bubble_sort_2ptr(fruits, len);
printf("排序后(二级指针传参):");
for (int i = 0; i < len; i++) {
printf("%s ", fruits[i]);
}
printf("\n");
return 0;
}
核心逻辑 :二级指针pp指向指针数组的首元素(即第一个字符串的地址),通过pp[j]访问第j个字符串的指针,交换时直接操作指针值。
示例2 二级指针传递动态二维数组(动态分配内存)
动态二维数组通过malloc分配:先分配行指针数组(int **arr),再为每行分配列空间。此时arr是二级指针,可直接传递给排序函数。
cpp
#include <stdio.h>
#include <stdlib.h>
// 冒泡排序动态二维数组(整体排序,视为一维数组)
// 参数:pp 动态二维数组首地址(二级指针),rows 行数,cols 列数
void bubble_sort_dynamic_2d(int **pp, int rows, int cols) {
int total = rows * cols;
int i, j, temp;
int swapped;
for (i = 0; i < total - 1; i++) {
swapped = 0;
for (j = 0; j < total - i - 1; j++) {
// 计算行列坐标(动态数组同样连续存储)
int row1 = j / cols, col1 = j % cols;
int row2 = (j + 1) / cols, col2 = (j + 1) % cols;
if (pp[row1][col1] > pp[row2][col2]) {
temp = pp[row1][col1];
pp[row1][col1] = pp[row2][col2];
pp[row2][col2] = temp;
swapped = 1;
}
}
if (!swapped) break;
}
}
// 打印动态二维数组
void print_dynamic_2d(int **pp, int rows, int cols) {
int i, j;
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
printf("%d ", pp[i][j]);
}
printf("\n");
}
}
int main() {
int rows = 3, cols = 4;
int **matrix = (int **)malloc(rows * sizeof(int *)); // 分配行指针数组
for (int i = 0; i < rows; i++) {
matrix[i] = (int *)malloc(cols * sizeof(int)); // 为每行分配列空间
}
// 初始化动态数组
int init_data[3][4] = {{9, 5, 7, 3}, {2, 8, 1, 6}, {4, 0, 10, 12}};
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = init_data[i][j];
}
}
printf("动态数组排序前:\n");
print_dynamic_2d(matrix, rows, cols);
bubble_sort_dynamic_2d(matrix, rows, cols); // 传递二级指针
printf("动态数组排序后(整体升序):\n");
print_dynamic_2d(matrix, rows, cols);
// 释放内存(避免泄漏)
for (int i = 0; i < rows; i++) free(matrix[i]);
free(matrix);
return 0;
}
核心逻辑 :动态二维数组的二级指针pp指向行指针数组,通过pp[i][j]访问元素,排序时视为一维数组处理(利用内存连续性)。
六、总结
| 数组类型 | 传递方式 | 本质(退化后) | 排序核心思路 | 典型场景 |
|---|---|---|---|---|
| 一维数组 | int arr[]或 int *arr |
一级指针 | 相邻元素比较交换 | 普通数值数组排序 |
| 二维数组(静态) | int arr[][N]或 int (*arr)[N] |
数组指针(行指针) | 视为一维数组整体排序 | 固定列数的矩阵排序 |
| 指针数组 | char *arr[]或 char **arr |
二级指针 | 交换指针(高效)或交换内容 | 字符串数组按字典序排序 |
| 动态二维数组 | int **arr |
二级指针 | 视为一维数组整体排序 | 运行时动态创建的多维数组排序 |
二级指针传参的关键 :明确其指向"指针数组"或"动态二维数组的行指针数组",通过解引用(pp[j]或pp[i][j])访问元素,排序逻辑与普通数组一致,仅传递方式不同。掌握二级指针传参,能灵活处理更复杂的数组场景(如动态内存、多级指针数据结构)。