【数据结构】第十九弹---C语言实现冒泡排序算法

news2024/11/25 4:27:01

 ✨个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】

目录

1、冒泡排序基本思想

2、代码的初步实现

3、代码的优化

4、代码的测试

5、时空复杂度分析

6、模拟实现qsort

6.1、冒泡排序函数

6.2、交换数据函数

6.3、比较函数

总结


1、冒泡排序基本思想

冒泡排序法:(Bubble sort)是一种基础的交换排序。对数组进行遍历,每次对相邻两个进行比较大小,若大的数值在前面则交换位置(升序),完成一趟遍历后数组中最大的数值到了数组的末尾位置,再对前面n-1个数值进行相同的遍历,完成n-1次遍历则排序完成。

1. 第一趟对0~n-1遍历,依次对比前后的大小,若是不满足前小后大就交换,此时最大的数就被挪到了最后一个位置。

2. 对0~n-2遍历,继续比较前后大小,此时前n-2个数中最大的数就到了倒数第二个位置。

3. 重复上述动作继续遍历,每一次都将最大的数向后挤,直到遍历完毕排序成功。

2、代码的初步实现

对int 类型的数进行升序排序。

//交换函数
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)//遍历n-1次
	{
		for (int j = 0; j < n - 1 - i; j++)//相邻两个数进行比较
		{
			if (a[j] > a[j + 1])//前面的值大于后面的值则交换
			{
				Swap(&a[j], &a[j + 1]);
			}
		}
	}
}

3、代码的优化

如果一次遍历,没有数据进行交换,则证明数组已经排好了顺序,不需要继续遍历,则引入exchange变量标志记录第一次遍历是否有数据交换。

void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		bool exchange = false;//默认false,值没变则没有交换
		for (int j = 0; j < n - 1 - i; j++)//遍历n-1次
		{
			if (a[j] > a[j + 1])//相邻两个数进行比较
			{
				Swap(&a[j], &a[j + 1]);//前面的值大于后面的值则交换
				exchange = true;
			}
		}
		if (exchange == false)//值没变则退出内循环
			break;
	}
}

4、代码的测试

测试代码:

//测试冒泡排序
int main()
{
	int a[] = { 9,8,7,6,5,4,3,2,1,0 };//给一组数据
	int sz = sizeof(a) / sizeof(a[0]);//计算数组元素个数
	printf("排序前:\n");
	ArrayPrint(a, sz);
	BubbleSort(a, sz);
	printf("排序后:\n");
	ArrayPrint(a, sz);
	return 0;
}

测试结果: 

5、时空复杂度分析

时间复杂度:

最坏情况:

当我们需要排升序的时候,原数组为降序,则为最坏情况。此时每次交换操作需要比较的次数从 n-1 次减少到 1 次,总共的比较次数是 (n-1) + (n-2) + … + 1 = n(n-1)/2,这是一个二次函数,因此时间复杂度为 O(n^2)。

最好情况:

当我们需要排升序时,原数组也是升序,我们只需要循环n次则可以判断结束,此时时间复杂度为O(N)。

由于时间复杂度取决于最坏情况,因此冒泡排序的时间复杂度为O(N^2)。

空间复杂度:

冒泡排序是一种原地排序算法,除了输入数组外,它只需要有限的几个变量(比如,交换标记和循环计数器)。因此,它的空间复杂度为常数空间O(1)。

6、模拟实现qsort

C语言中库函数 qsort是通过函数指针cmp传入数据类型的比较方式,实现对各种数据类型都能进行排序的功能。

我们将模仿qsort函数使用冒泡排序算法实现对各种数据类型都能进行排序的函数,并且使用const关键字严格限制参的属性,达到很高的健壮性要求。

6.1、冒泡排序函数

库函数qsort()函数接口:

void qsort (void* base, size_t num, size_t size,
            int (*com)(const void*,const void*));

模拟实现的函数接口:

void bubble_sort(void* base, //待排序数组首元素地址
                 size_t num, //待排序数组元素个数
                 size_t size,//待排序数组元素类型大小,单位为字节
                 int (*com)(const void*,const void*)//函数指针 如何进行比较函数
);

6.2、交换数据函数

void swap(char* buf1, char* buf2, size_t size);

思想:

以1个字节为单位对两个指针指向的内容进行交换交换size次即可。

参数:

buf1:被交换的数据的地址。
buf2:被交换的数据的地址。
size:被交换数据类型的字节大小。

void swap(char* buf1, char* buf2, size_t size)
{
	assert(buf1 && buf2);//断言,指针不为空才能交换
	size_t i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

6.3、比较函数

int cmp(const void* e1, const void* e2);

 void*是一个空类型的指针,可以存放任意类型的指针。

此处就用到了void*,void*为空指针,不能直接使用但是可以强转为其他的任何类型,那么此处我们应该强转成什么类型呢?直接强转成int*?很显然,如果强转为int*,那么char*,short*就不好进行转化了,因此此处转化为char*,如果要用到其他的类型,我们通过+数据类型大小就可以得到因此我们需要将指针转换成char*,依次按照字节进行交换。

返回值:

大于0,e1大;等于0,一样大;小于0,e2大。

参数:

e1:被比较的数据的地址,由void*指针接收,由const限制不能改变指针指向,但可以改变指针指向的内容。
e1:被比较的数据的地址,由void*指针接收,由const限制不能改变指针指向,但可以改变指针指向的内容。

函数体:

用户自定义实现数值的比较规则。

传参:

1. 被比较数值的地址由void*指针接收。

2. 数值在数组中第 i 个位置:将void*转换成char指针,(char*)base + i*size 。

一些规则的演示:

//int类型数据比较(升序)
int cmp(const void* e1, const void* e2)
{
    return *(int*)e1 - *(int*)e2;
}

//int类型数据比较(降序)
int cmp(const void* e1, const void* e2)
{
    return *(int*)e2 - *(int*)e1;	//降序就是把e1,e2的位置交换一下
}

//字符串比较(按字母升序)
#include <string.h>
int cmp(const void* e1, const void* e2)
{
    return strcmp((char*)e1, (char*)e2);	//字符串比较函数,与前面的比较规则一致
}

冒泡排序法的实现

#include <assert.h>		//引入头文件<assert.h>,使用assert函数断言

//交换数据
void swap(char* buf1, char* buf2, size_t size)
{
	assert(buf1 && buf2);//断言,指针不为空才能交换
	size_t i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

//冒泡排序法
void bubble_sort(void* base,size_t num,size_t size,int (*cmp)(const void* e1,const void* e2))
{
	size_t i = 0;
	for (i = 0; i < num - 1; i++)
	{
		size_t j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			//if (arr[j] > arr[j + 1])
			//(char*)base+j*size,(char*)base+(j+1)*size
			if(cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0)
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size,size);
			}
		}
	}
}

1.整型数组降序排序的演示

//整型降序比较函数
int cmp_int(void* e1, void* e2)
{
	return *((int*)e2) - *((int*)e1);
}

void test1()
{
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print_arr(arr, sz);//打印数组元素
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	print_arr(arr, sz);//打印数组元素
}

测试结果: 

2.结构体演示 

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

int cmp_stu_by_age(const void* e1,const void* e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}

void test2()
{
	struct Stu arr[] = { {"zhangsan",18},{"lisi",32},{"wangwu",20} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}

测试结果: 

总结


本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1849604.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android SurfaceFlinger——服务启动流程(二)

SurfaceFlinger 是 Android 系统中的一个核心服务&#xff0c;负责管理图形缓冲区的合成和屏幕显示&#xff0c;是 Android 图形系统的关键组件。 一、启动流程 SurfaceFlinger 作为一个系统服务&#xff0c;在 Android 启动早期由 init 进程通过 servicemanager 启动。它是作…

Vue3中的常见组件通信(超详细版)

Vue3中的常见组件通信 概述 ​ 在vue3中常见的组件通信有props、mitt、v-model、 r e f s 、 refs、 refs、parent、provide、inject、pinia、slot等。不同的组件关系用不同的传递方式。常见的撘配形式如下表所示。 组件关系传递方式父传子1. props2. v-model3. $refs4. 默认…

[Django学习]前端+后端两种方式处理图片流数据

方式1&#xff1a;数据库存放图片地址,图片存放在Django项目文件中 1.首先&#xff0c;我们现在models.py文件中定义模型来存放该图片数据,前端传来的数据都会存放在Django项目文件里的images文件夹下 from django.db import modelsclass Image(models.Model):title models.C…

深度神经网络——什么是小样本学习?

引言 小样本学习是指使用极少量的训练数据来开发人工智能模型的各种算法和技术。小样本学习致力于让人工智能模型在接触相对较少的训练实例后识别和分类新数据。小样本训练与训练机器学习模型的传统方法形成鲜明对比&#xff0c;传统方法通常使用大量训练数据。小样本学习是 主…

aws的alb,多个域名绑定多个网站实践

例如首次创建的alb负载均衡只有www.xxx.com 需要添加 负载 test2.xxx.com aws的Route 53产品解析到负载均衡 www.xxx.com 添加CNAME&#xff0c;到负载均衡的dns字段axx test2.xxx.com 添加CNAME&#xff0c;到负载均衡的dns字段axx 主要介绍目标组和规则 创建alb就不介…

MacOS 中 Agent 图标删除

这个是战网没有完全卸载赶紧导致的 在访达中点击前往文件夹&#xff0c;输入&#xff1a; /Users/Shared将对应的目录删掉即可。会提示需要输入密码。

Java 从零开始写一个简单的图书管理系统

了解一下 先来了解要实现一个怎样的图书管理系统 从中可以看到有操作的 使用者 和 不同 的 功能 而不同的使用者有不同的 菜单 那要如何实现呢&#xff1f; 请继续看下去 如何实现 首先了解我们 需要什么 图书系统需要 书&#xff0c;放书的 书架 &#xff0c;用户 中有 管…

Nutch爬虫在大数据采集中的应用案例

引言 在当今信息爆炸的时代&#xff0c;大数据的价值日益凸显。网络作为信息的海洋&#xff0c;蕴藏着丰富的数据资源。Nutch&#xff0c;作为一个开源的Java编写的网络爬虫框架&#xff0c;以其高效的数据采集能力和良好的可扩展性&#xff0c;成为大数据采集的重要工具。本文…

系统烧写工具--MfgTool

系统烧写工具--MfgTool 1 介绍1.1 概述1.2 UUU 特性1.3 UUU 功能1.4 UUU 命令1.5 MFGTools 功能 2 MFGTools 目录结构及说明2.1 MFGTools 目录结构重要文件烧写自己系统 2.2 说明2.3 分析配置文件2.3.1 UiCfg.ini2.3.2 cfg.ini2.3.3 ucl2.xml 3 MfgTool 工作流程4 烧录流程4.1 …

mysql--安装跳过验证修改密码安全加固

安装mysql 配置mysql的yum源 [rootVM-0-14-rockylinux ~]# tee /etc/yum.repos.d/mysql.repo << EOF > [MYSQL] > namemysql > baseurlhttps://mirrors.tuna.tsinghua.edu.cn/mysql/yum/mysql-5.7-community-el7-x86_64 > gpgcheck0 > EOF yum安装mysq…

python项目(课设)——飞机大战小游戏项目源码(pygame)

主程序 import pygame from plane_sprites import * class PlaneGame: """ 游戏类 """ def __init__(self): print("游戏初始化") # 初始化字体模块 pygame.font.init() # 创建游戏…

eclipse中svn从分支合并到主干及冲突解决

1、将分支先commit&#xff0c;然后再update&#xff0c;然后再clean一下&#xff0c;将项目多余的target都清理掉。 2、将branches切换到trunk 3、工程上右键-》Team-》合并&#xff08;或Merge&#xff09; 4、默认选项&#xff0c;点击Next 5、有未提交的改动&#xff0c;…

兴顺物流管理系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;驾驶员管理&#xff0c;物流资讯管理&#xff0c;车辆管理&#xff0c;基础数据管理 员工账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;物流资讯管理&…

尚品汇-(三)

maven之packaging标签 &#xff08;1&#xff09;项目创建父模块 首先设置 下Maven Maven&#xff1a;仓库地址&#xff1a;这里是腾讯云仓库 作为父模块&#xff0c;src没用&#xff0c;干掉src 这里我们是Maven创建的项目&#xff0c;想要项目变成SpringBoot的项目&#xf…

Mybatis 系列全解(1)——全网免费最细最全,手把手教,学完就可做项目!

Mybatis 系列全解&#xff08;1&#xff09; 1. 第一个小程序2. CURD 增删改查3. 模糊查询4. 配置解析4.1 核心配置文件4.2 环境配置4.3 属性4.4 类型别名4.5 设置4.6 映射器 mappers 1. 第一个小程序 1&#xff09;创建一个数据库&#xff0c;一个表&#xff0c;填入一些数据…

uni app 树状结构数据展示

树状数据展示&#xff0c;可以点击item 将点击数据给父组件 &#xff0c;满足自己需求。不喜勿喷&#xff0c;很简单可以根据自己需求改哈&#xff0c;不要问&#xff0c;点赞收藏就好。其实可以和上一篇文章uni app 自定义 带popup弹窗的input组件-CSDN博客结合使用&#xff…

LabVIEW项目管理中如何平衡成本、时间和质量

在LabVIEW项目管理中&#xff0c;平衡成本、时间和质量是实现项目成功的关键。通过制定详细的项目计划、合理分配资源、严格控制进度、进行质量保证和灵活应对变化&#xff0c;项目管理者可以有效地协调这三者的关系&#xff0c;确保项目按时、按质、按预算完成。 1. 制定详细…

Reid系列论文学习——换装Reid

今天要学习的有关Reid的论文是2019年提出的一篇名为&#xff1a;Beyond Scalar Neuron: Adopting Vector-Neuron Capsules for Long-Term Person Re-Identification. 论文链接&#xff1a;https://opus.lib.uts.edu.au/bitstream/10453/137156/4/Binder1.pdf Code链接&#x…

搜索python包的说明

当我发现bug时&#xff0c;就怀疑是sns包的版本问题了&#xff08;原代码是原作者以前成功运行的代码&#xff09;&#xff0c;于是直接到网上搜&#xff0c;找到对应的说明文档 根据该示例代码进行改写&#xff1a; 达成目的。

Elasticsearch 数据提取 - 最适合这项工作的工具是什么?

作者&#xff1a;来自 Elastic Josh Asres 了解在 Elasticsearch 中为你的搜索用例提取数据的所有不同方式。 对于搜索用例&#xff0c;高效采集和处理来自各种来源的数据的能力至关重要。无论你处理的是 SQL 数据库、CRM 还是任何自定义数据源&#xff0c;选择正确的数据采集…