冒泡排序(基础版+通用版)

1 基础版

cpp 复制代码
//冒泡排序
#include <stdio.h>
bubble_sort(int arr[], int len)
{
    int i = 0;
    int j = 0;

    for (i = 0; i < len - 1; i++)
    {
        int flag = 1;
        for (j = 0; j < len - i - 1; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                arr[j] = arr[j] ^ arr[j + 1];
                arr[j + 1] = arr[j] ^ arr[j + 1];
                arr[j] = arr[j] ^ arr[j + 1];
                //等价于:
                /*int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;*/
                flag = 0;
            }
        }
        if (flag == 1)
            break;
    }
}
int main()
{
    int arr[] = { 3,1,7,5,8,9,0,2,4,6 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    bubble_sort(arr, sz);
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

冒泡排序的核心思路

冒泡排序的本质是通过相邻元素的比较和交换,让大的元素像 "气泡" 一样逐步 "浮" 到数组的末尾,每一轮循环都会确定一个最大元素的最终位置,直到整个数组有序。

  1. 外层循环:控制排序的轮数,一个长度为 len 的数组,最多需要 len-1 轮排序(因为最后一个元素会自动归位)。

代码中的数组长度是 10,最多需要 9 轮排序,每轮确定一个最大数的位置(第 1 轮把最大数放到最后,第 2 轮把次大数放到倒数第 2 位,以此类推)

第一轮循环:

  1. 内层循环:每一轮的相邻元素比较交换

思路:每完成一轮排序,末尾的 i 个元素已经是有序的(最大的 i 个数),所以内层循环只需比较前 len-i-1 个元素,避免重复比较

举例:第 1 轮(i=0),内层循环比较 0~8 索引(共 9 次),把最大数放到索引 9;第 2 轮(i=1),内层循环比较 0~7 索引(共 8 次),把次大数放到索引 8,以此类推。

  1. (优化)提前终止已排好序的循环(flag 标志位)
  • 初始化 flag=1:假设本轮循环中没有任何元素交换(即数组已经有序)。
    • 如果发生交换,说明数组还没排好,把 flag=0
    • 本轮循环结束后,如果 flag 还是 1,说明没有任何交换发生,数组已经完全有序,直接跳出外层循环,不用继续后面的轮次。
  • 举例:如果你的数组本来就是 {0,1,2,3,4},第一轮循环就不会有任何交换,flag=1,直接终止排序,不用执行剩下的 4 轮循环,提升效率。

2.通用版冒泡排序(maopao2)

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
void Swap(char*buf1,char*buf2,size_t width)
{
	for (int i = 0; i < width; i++)
	{
		char temp = *buf1;
		*buf1 = *buf2;
		*buf2 = temp;
		buf1++;
		buf2++;
	}
}
void maopao2(void * base, size_t sz,size_t width,int (*cmp)(const void*p1,const void*p2 ))
{
	for (int i = 0; i < sz - 1; i++)
	{
		for (int j = 0; j < sz - i - 1; j++)
		{
//排序函数的输入是void* base(无类型指针),它本身不支持算术运算(不能直接base+1),
// 也不知道数组元素的具体类型,所以必须通过char*来实现 "精准字节级定位"。
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				//交换两个元素
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);

			}
		}
	}
	
}

struct student
{
	int age;
	char name[20];
};

int cmp(const void* a, const void* b)
{
	return *(int*)b - *(int*)a;
}

int cmp2(const void* a, const void* b)
{
	return strcmp(((struct student*)a)->name, ((struct student*)b)->name);
}


int main()
{
	int a[10] = { 10,90,30,44,450,60,70,80,20,109 };
	int sz = sizeof(a) / sizeof(a[0]);


	struct student st[] = {{20,"xiaoming"},{25,"aiaohong"},{30,"ziaoli"}};
	int st_sz = sizeof(st) / sizeof(st[0]);
	maopao2(st, st_sz, sizeof(st[0]), cmp2);

	for(int i=0;i<3;i++)
	{	
		printf("%s ",st[i].name);
	}
	printf("\n");

	maopao2(a, sz, sizeof(a[0]), cmp);

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
	return 0;
}

优化:

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS  // 消除VS环境下的安全函数警告
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 功能:字节级交换两个元素(支持任意类型)
// 参数:buf1-第一个元素地址,buf2-第二个元素地址,width-单个元素的字节大小
void Swap(char* buf1, char* buf2, size_t width)
{
    // 按字节交换:循环width次,确保不同类型(int/struct等)都能完整交换
    for (int i = 0; i < width; i++)
    {
        char temp = *buf1;
        *buf1 = *buf2;
        *buf2 = temp;
        buf1++; // 指针移动1字节(char*是最小字节单位,保证精准定位)
        buf2++;
    }
}

// 通用版冒泡排序:支持任意类型数组排序
// 参数:
//  base-数组首地址(void*无类型指针,可接收任意类型)
//  sz-数组元素个数
//  width-单个元素的字节大小(通过sizeof计算)
//  cmp-比较函数指针(自定义排序规则:返回>0则交换,=0相等,<0不交换)
void maopao2(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{
    // 外层循环:控制排序趟数
    for (int i = 0; i < sz - 1; i++)
    {
        int flag = 1; // 优化标记:标记本趟是否发生交换
        // 内层循环:控制每趟比较次数,逐趟减少i次
        for (int j = 0; j < sz - i - 1; j++)
        {
            // 关键:void*指针不能直接算术运算(无类型则无字节大小概念)
            // 转换为char*:按1字节偏移,通过 j*width 定位第j个元素,(j+1)*width定位第j+1个元素
            if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
            {
                // 调用字节交换函数,交换两个元素
                Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
                flag = 0; // 发生交换,重置标记
            }
        }
        if (flag == 1) // 本趟无交换,数组已有序,提前退出
            break;
    }
}

// 定义学生结构体(用于测试自定义类型排序)
struct student
{
    int age;
    char name[20];
};

// 比较函数1:int类型降序排序
// 返回值>0:p1对应的值 > p2对应的值,需要交换(实现降序)
int cmp_int_desc(const void* a, const void* b)
{
    // 先将void*转换为int*,再解引用获取值(必须强转,否则无法解引用)
    return *(int*)b - *(int*)a;
}

// 比较函数2:学生结构体按name升序排序(字典序)
int cmp_student_name(const void* a, const void* b)
{
    // 转换为struct student*,通过->访问name成员,调用strcmp比较字符串
    return strcmp(((struct student*)a)->name, ((struct student*)b)->name);
}

int main()
{
    // 测试1:int数组降序排序
    int a[10] = { 10,90,30,44,450,60,70,80,20,109 };
    int sz_int = sizeof(a) / sizeof(a[0]);
    maopao2(a, sz_int, sizeof(a[0]), cmp_int_desc);
    printf("通用版排序结果(int降序):");
    for (int i = 0; i < sz_int; i++)
    {
        printf("%d ", a[i]);
    }
    printf("\n");

    // 测试2:学生结构体按name升序排序
    struct student st[] = { {20,"xiaoming"}, {25,"aiaohong"}, {30,"ziaoli"} };
    int sz_st = sizeof(st) / sizeof(st[0]);
    maopao2(st, sz_st, sizeof(st[0]), cmp_student_name);
    printf("通用版排序结果(学生name升序):");
    for (int i = 0; i < sz_st; i++)
    {
        printf("%s ", st[i].name);
    }
    printf("\n");

    return 0;
}
    
  • void*无类型指针:作为函数参数可接收任意类型的数组首地址,但不能直接算术运算(无字节大小信息),必须转换为char*(1字节单位)才能精准定位元素。

  • 字节级交换(Swap函数):通过循环交换每个字节,确保无论元素类型是4字节的int还是24字节的student结构体,都能完整交换。

  • 函数指针(cmp):将排序规则与排序算法解耦,用户只需实现不同的比较函数,就能实现升序、降序或自定义类型排序,大幅提升灵活性。

相关推荐
C雨后彩虹2 小时前
无向图染色
java·数据结构·算法·华为·面试
坚持就完事了3 小时前
扫描线算法
算法
鱼跃鹰飞3 小时前
Leetcode尊享面试100题:252. 会议室
算法·leetcode·面试
程序员-King.3 小时前
二分查找——算法总结与教学指南
数据结构·算法
Zevalin爱灰灰3 小时前
现代控制理论——第三章 线性控制系统的能控性和能观性
线性代数·算法·现代控制
kklovecode3 小时前
C语言之头文件,宏和条件编译
c语言·开发语言·算法
Xの哲學3 小时前
Linux自旋锁深度解析: 从设计思想到实战应用
linux·服务器·网络·数据结构·算法
晚风吹长发3 小时前
深入理解Linux中用户缓冲区,文件系统及inode
linux·运维·算法·链接·缓冲区·inode
cwplh3 小时前
DP 优化一:单调队列优化 DP
算法